Merge pull request #83 from modelstudioai/feat/composable-cli

Release 1.5.0: finetune / deploy / dataset / token-plan + composable CLI
This commit is contained in:
Gong Shiqi
2026-07-01 15:54:13 +08:00
committed by GitHub
194 changed files with 2234 additions and 1443 deletions
+1
View File
@@ -12,6 +12,7 @@ node_modules
dist
dist-ssr
tools/generated
.node-version
*.local
+20 -1
View File
@@ -2,10 +2,29 @@
All notable changes to `bailian-cli` and `bailian-cli-core` are documented here.
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). The two packages share a single version number — they are always released together.
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). The `bailian-cli`, `bailian-cli-core`, `bailian-cli-runtime`, and `bailian-cli-commands` packages share a single version number — they are always released together.
[中文版](CHANGELOG.zh.md) · [README](README.md) · [Contributing](CONTRIBUTING.md)
## [1.5.0] - 2026-07-01
### Added
- Model fine-tuning — `bl finetune`: create, list, get, watch, and cancel jobs; fetch training logs; list checkpoints; export a checkpoint as a deployable model; and query training capability (by model or by training type). Supports `sft`, `sft-lora`, `dpo`, `dpo-lora`, and `cpt` training types.
- Model deployment — `bl deploy`: create, list, get, update (rate limits), scale, and delete deployments; list deployable models and plans.
- Dataset management — `bl dataset`: upload, list, get, and delete dataset files, plus `bl dataset validate` to check a local `.jsonl` before uploading (ChatML / DPO / CPT formats).
- Token Plan management — `bl token-plan`: list subscription seats, add members, batch-assign seats, and create a per-seat API key.
- Automatic update check: after a command finishes, the CLI checks npm for a newer release (throttled) and shows an `Update available` hint; a major stable-version gap upgrades itself automatically. Skipped with `--quiet` or when running `bl update`.
- Composable packages: `bailian-cli-runtime` (CLI framework) and `bailian-cli-commands` (command library) are now published alongside `bailian-cli-core`, and a new sibling CLI `knowledge-studio-cli` (`kscli`) ships on top of them. `bl` behavior is unchanged.
### Removed
- `bl config export-schema` (exported CLI commands as Anthropic/OpenAI-compatible JSON tool schemas) has been removed.
### Fixed
- Console gateway commands (`bl console call`, etc.) now surface a readable message when the gateway returns a non-string `errorCode`, instead of `[object Object]`.
## [1.4.2] - 2026-06-24
### Added
+20 -1
View File
@@ -2,10 +2,29 @@
`bailian-cli` 和 `bailian-cli-core` 的所有重要变更都记录在此。
格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/spec/v2.0.0.html)。两个包共享一个版本号,总是一起发布。
格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/spec/v2.0.0.html)。`bailian-cli`、`bailian-cli-core`、`bailian-cli-runtime`、`bailian-cli-commands` 共享一个版本号,总是一起发布。
[English](CHANGELOG.md) · [README](README.zh.md) · [参与贡献](CONTRIBUTING.zh.md)
## [1.5.0] - 2026-07-01
### 新增
- 模型精调 —— `bl finetune`:创建、列出、查询、观察、取消训练任务;拉取训练日志;列出 checkpoint;将 checkpoint 导出为可部署模型;查询训练能力(按模型或按训练类型)。支持 `sft`、`sft-lora`、`dpo`、`dpo-lora`、`cpt` 训练类型。
- 模型部署 —— `bl deploy`:创建、列出、查询、更新(限流)、扩缩容、删除部署;列出可部署模型与套餐。
- 数据集管理 —— `bl dataset`:上传、列出、查询、删除数据集文件,并新增 `bl dataset validate` 在上传前本地校验 `.jsonl`(ChatML / DPO / CPT 格式)。
- Token Plan 管理 —— `bl token-plan`:列出订阅座位、添加成员、批量分配座位、为座位创建 API Key。
- 自动更新检查:命令执行完成后,CLI 会(节流地)检查 npm 上是否有新版本并提示 `Update available`;若与稳定版存在大版本差距则自动升级。`--quiet` 或执行 `bl update` 时跳过。
- 可组合包:`bailian-cli-runtime`(CLI 框架)与 `bailian-cli-commands`(命令库)现在与 `bailian-cli-core` 一起发布,并在其之上新增了同家族 CLI `knowledge-studio-cli`(`kscli`)。`bl` 行为保持不变。
### 已移除
- 移除 `bl config export-schema` 命令(原用于把 CLI 命令导出为 Anthropic/OpenAI 兼容的 JSON tool schema)。
### 修复
- 控制台网关类命令(`bl console call` 等)在网关返回非字符串 `errorCode` 时,现在会给出可读的错误信息,而不是 `[object Object]`。
## [1.4.2] - 2026-06-24
### 新增
+2 -1
View File
@@ -16,9 +16,10 @@
"ready": "vp check && vp run -r test && vp run -r build",
"prepare": "vp config",
"check": "vp check",
"sync:skill-assets": "pnpm --filter bailian-cli-core run build && pnpm --filter bailian-cli run generate:reference && pnpm --filter bailian-cli run sync:skill-version",
"sync:skill-assets": "pnpm --filter \"bailian-cli^...\" run build && pnpm --filter bailian-cli run generate:reference && pnpm --filter bailian-cli run sync:skill-version",
"dev": "pnpm -F bailian-cli-core dev",
"bl": "pnpm -F bailian-cli dev",
"kscli": "pnpm -F knowledge-studio-cli dev",
"test": "vp test",
"release:check": "node tools/release/check.mjs",
"wiki:crawl": "node tools/wiki-crawler/index.mjs",
+11 -16
View File
@@ -1,6 +1,6 @@
{
"name": "bailian-cli",
"version": "1.4.2",
"version": "1.5.0",
"description": "CLI for Aliyun Model Studio (DashScope) AI Platform.",
"keywords": [
"agent",
@@ -33,6 +33,10 @@
"./package.json": "./package.json"
},
"publishConfig": {
"exports": {
".": "./dist/bailian.mjs",
"./package.json": "./package.json"
},
"registry": "https://registry.npmjs.org/"
},
"scripts": {
@@ -44,32 +48,23 @@
"check": "vp check"
},
"dependencies": {
"bailian-cli-commands": "workspace:*",
"bailian-cli-core": "workspace:*",
"boxen": "catalog:",
"chalk": "catalog:",
"undici": "catalog:"
"bailian-cli-runtime": "workspace:*"
},
"devDependencies": {
"@clack/prompts": "^0.7.0",
"@types/node": "catalog:",
"@typescript/native-preview": "7.0.0-dev.20260328.1",
"ajv": "catalog:",
"boxen": "catalog:",
"chalk": "catalog:",
"typescript": "^6.0.2",
"vite-plus": "catalog:",
"undici": "catalog:",
"vite-plus": "0.1.22",
"yaml": "catalog:"
},
"engines": {
"node": ">=22.12.0"
},
"inlinedDependencies": {
"@clack/core": "0.3.5",
"@clack/prompts": "0.7.0",
"ajv": "8.20.0",
"fast-deep-equal": "3.1.3",
"fast-uri": "3.1.2",
"json-schema-traverse": "1.0.0",
"picocolors": "1.1.1",
"sisteransi": "1.0.5",
"yaml": "2.8.3"
}
}
+153
View File
@@ -0,0 +1,153 @@
import type { Command } from "bailian-cli-core";
import {
authLogin,
authStatus,
authLogout,
textChat,
textOmni,
imageGenerate,
imageEdit,
videoGenerate,
videoEdit,
videoRef,
videoTaskGet,
videoDownload,
visionDescribe,
configShow,
configSet,
update,
appCall,
appList,
memoryAdd,
memorySearch,
memoryList,
memoryUpdate,
memoryDelete,
memoryProfileCreate,
memoryProfileGet,
knowledgeRetrieve,
mcpCall,
mcpList,
mcpTools,
searchWeb,
speechSynthesize,
speechRecognize,
fileUpload,
consoleCall,
usageFree,
usageFreetier,
usageStats,
pipelineRun,
pipelineValidate,
advisorRecommend,
workspaceList,
quotaList,
quotaRequest,
quotaHistory,
quotaCheck,
datasetUpload,
datasetList,
datasetGet,
datasetDelete,
datasetValidate,
finetuneCreate,
finetuneList,
finetuneGet,
finetuneCancel,
finetuneDelete,
finetuneLogs,
finetuneCheckpoints,
finetuneExport,
finetuneWatch,
finetuneCapability,
deployCreate,
deployList,
deployGet,
deployModels,
deployScale,
deployUpdate,
deployDelete,
tokenPlanListSeats,
tokenPlanCreateKey,
tokenPlanAssignSeats,
tokenPlanAddMember,
} from "bailian-cli-commands";
// Full bailian-cli product: every command, exposed under the `bl` binary.
// The command paths below are this product's decision — the command library
// ships no presets, so the map is spelled out here. Kept in its own module
// (no side effects) so tools like generate-reference.ts can import it without
// starting the CLI.
export const commands: Record<string, Command> = {
"auth login": authLogin,
"auth status": authStatus,
"auth logout": authLogout,
"text chat": textChat,
omni: textOmni,
"image generate": imageGenerate,
"image edit": imageEdit,
"video generate": videoGenerate,
"video edit": videoEdit,
"video ref": videoRef,
"video task get": videoTaskGet,
"video download": videoDownload,
"vision describe": visionDescribe,
"config show": configShow,
"config set": configSet,
update,
"app call": appCall,
"app list": appList,
"memory add": memoryAdd,
"memory search": memorySearch,
"memory list": memoryList,
"memory update": memoryUpdate,
"memory delete": memoryDelete,
"memory profile create": memoryProfileCreate,
"memory profile get": memoryProfileGet,
"knowledge retrieve": knowledgeRetrieve,
"mcp call": mcpCall,
"mcp list": mcpList,
"mcp tools": mcpTools,
"search web": searchWeb,
"speech synthesize": speechSynthesize,
"speech recognize": speechRecognize,
"file upload": fileUpload,
"console call": consoleCall,
"usage free": usageFree,
"usage freetier": usageFreetier,
"usage stats": usageStats,
"pipeline run": pipelineRun,
"pipeline validate": pipelineValidate,
"advisor recommend": advisorRecommend,
"workspace list": workspaceList,
"quota list": quotaList,
"quota request": quotaRequest,
"quota history": quotaHistory,
"quota check": quotaCheck,
"dataset upload": datasetUpload,
"dataset list": datasetList,
"dataset get": datasetGet,
"dataset delete": datasetDelete,
"dataset validate": datasetValidate,
"finetune create": finetuneCreate,
"finetune list": finetuneList,
"finetune get": finetuneGet,
"finetune cancel": finetuneCancel,
"finetune delete": finetuneDelete,
"finetune logs": finetuneLogs,
"finetune checkpoints": finetuneCheckpoints,
"finetune export": finetuneExport,
"finetune watch": finetuneWatch,
"finetune capability": finetuneCapability,
"deploy create": deployCreate,
"deploy list": deployList,
"deploy get": deployGet,
"deploy models": deployModels,
"deploy scale": deployScale,
"deploy update": deployUpdate,
"deploy delete": deployDelete,
"token-plan list-seats": tokenPlanListSeats,
"token-plan create-key": tokenPlanCreateKey,
"token-plan assign-seats": tokenPlanAssignSeats,
"token-plan add-member": tokenPlanAddMember,
};
-150
View File
@@ -1,150 +0,0 @@
import type { Command } from "bailian-cli-core";
import authLogin from "./auth/login.ts";
import authStatus from "./auth/status.ts";
import authLogout from "./auth/logout.ts";
import textChat from "./text/chat.ts";
import textOmni from "./omni/chat.ts";
import imageGenerate from "./image/generate.ts";
import imageEdit from "./image/edit.ts";
import videoGenerate from "./video/generate.ts";
import videoEdit from "./video/edit.ts";
import videoRef from "./video/ref.ts";
import videoTaskGet from "./video/task-get.ts";
import videoDownload from "./video/download.ts";
import visionDescribe from "./vision/describe.ts";
import configShow from "./config/show.ts";
import configSet from "./config/set.ts";
import configExportSchema from "./config/export-schema.ts";
import update from "./update.ts";
import appCall from "./app/call.ts";
import appList from "./app/list.ts";
import memoryAdd from "./memory/add.ts";
import memorySearch from "./memory/search.ts";
import memoryList from "./memory/list.ts";
import memoryUpdate from "./memory/update.ts";
import memoryDelete from "./memory/delete.ts";
import memoryProfileCreate from "./memory/profile-create.ts";
import memoryProfileGet from "./memory/profile-get.ts";
import knowledgeRetrieve from "./knowledge/retrieve.ts";
import mcpCall from "./mcp/call.ts";
import mcpList from "./mcp/list.ts";
import mcpTools from "./mcp/tools.ts";
import searchWeb from "./search/web.ts";
import speechSynthesize from "./speech/synthesize.ts";
import speechRecognize from "./speech/recognize.ts";
import fileUpload from "./file/upload.ts";
import datasetUpload from "./dataset/upload.ts";
import datasetList from "./dataset/list.ts";
import datasetGet from "./dataset/get.ts";
import datasetDelete from "./dataset/delete.ts";
import datasetValidate from "./dataset/validate.ts";
import finetuneCreate from "./finetune/create.ts";
import finetuneList from "./finetune/list.ts";
import finetuneGet from "./finetune/get.ts";
import finetuneCancel from "./finetune/cancel.ts";
import finetuneDelete from "./finetune/delete.ts";
import finetuneLogs from "./finetune/logs.ts";
import finetuneCheckpoints from "./finetune/checkpoints.ts";
import finetuneExport from "./finetune/export.ts";
import finetuneWatch from "./finetune/watch.ts";
import finetuneCapability from "./finetune/capability.ts";
import deployCreate from "./deploy/create.ts";
import deployList from "./deploy/list.ts";
import deployGet from "./deploy/get.ts";
import deployModels from "./deploy/models.ts";
import deployScale from "./deploy/scale.ts";
import deployUpdate from "./deploy/update.ts";
import deployDelete from "./deploy/delete.ts";
import consoleCall from "./console/call.ts";
import usageFree from "./usage/free.ts";
import usageFreetier from "./usage/freetier.ts";
import usageStats from "./usage/stats.ts";
import pipelineRun from "./pipeline/run.ts";
import pipelineValidate from "./pipeline/validate.ts";
import advisorRecommend from "./advisor/recommend.ts";
import workspaceList from "./workspace/list.ts";
import quotaList from "./quota/list.ts";
import quotaRequest from "./quota/request.ts";
import quotaHistory from "./quota/history.ts";
import quotaCheck from "./quota/check.ts";
import tokenPlanListSeats from "./token-plan/list-seats.ts";
import tokenPlanCreateKey from "./token-plan/create-key.ts";
import tokenPlanAssignSeats from "./token-plan/assign-seats.ts";
import tokenPlanAddMember from "./token-plan/add-member.ts";
/** Command registry map (no dependency on registry.ts — safe for build-time import). */
export const commands: Record<string, Command> = {
"auth login": authLogin,
"auth status": authStatus,
"auth logout": authLogout,
"text chat": textChat,
omni: textOmni,
"image generate": imageGenerate,
"image edit": imageEdit,
"video generate": videoGenerate,
"video edit": videoEdit,
"video ref": videoRef,
"video task get": videoTaskGet,
"video download": videoDownload,
"vision describe": visionDescribe,
"app call": appCall,
"app list": appList,
"memory add": memoryAdd,
"memory search": memorySearch,
"memory list": memoryList,
"memory update": memoryUpdate,
"memory delete": memoryDelete,
"memory profile create": memoryProfileCreate,
"memory profile get": memoryProfileGet,
"knowledge retrieve": knowledgeRetrieve,
"mcp list": mcpList,
"mcp tools": mcpTools,
"mcp call": mcpCall,
"search web": searchWeb,
"speech synthesize": speechSynthesize,
"speech recognize": speechRecognize,
"file upload": fileUpload,
"dataset upload": datasetUpload,
"dataset list": datasetList,
"dataset get": datasetGet,
"dataset delete": datasetDelete,
"dataset validate": datasetValidate,
"finetune create": finetuneCreate,
"finetune list": finetuneList,
"finetune get": finetuneGet,
"finetune cancel": finetuneCancel,
"finetune delete": finetuneDelete,
"finetune logs": finetuneLogs,
"finetune checkpoints": finetuneCheckpoints,
"finetune export": finetuneExport,
"finetune watch": finetuneWatch,
"finetune capability": finetuneCapability,
"deploy create": deployCreate,
"deploy list": deployList,
"deploy get": deployGet,
"deploy models": deployModels,
"deploy scale": deployScale,
"deploy update": deployUpdate,
"deploy delete": deployDelete,
"console call": consoleCall,
"usage free": usageFree,
"usage freetier": usageFreetier,
"usage stats": usageStats,
"pipeline run": pipelineRun,
"pipeline validate": pipelineValidate,
"config show": configShow,
"config set": configSet,
"config export-schema": configExportSchema,
"advisor recommend": advisorRecommend,
"workspace list": workspaceList,
"quota list": quotaList,
"quota request": quotaRequest,
"quota history": quotaHistory,
"quota check": quotaCheck,
"token-plan list-seats": tokenPlanListSeats,
"token-plan create-key": tokenPlanCreateKey,
"token-plan assign-seats": tokenPlanAssignSeats,
"token-plan add-member": tokenPlanAddMember,
update: update,
};
@@ -1,47 +0,0 @@
import { defineCommand, generateToolSchema } from "bailian-cli-core";
import type { Config } from "bailian-cli-core";
import type { GlobalFlags } from "bailian-cli-core";
import { BailianError } from "bailian-cli-core";
import { ExitCode } from "bailian-cli-core";
/**
* Commands that are infrastructure/auth-related and not suitable as Agent tools.
*/
const SKIP_PREFIXES = ["auth ", "config ", "update"];
export default defineCommand({
name: "config export-schema",
description:
"Export all (or one) CLI command(s) as Anthropic/OpenAI-compatible JSON tool schemas",
skipDefaultApiKeySetup: true,
usage: 'bl config export-schema [--command "<name>"]',
options: [
{
flag: "--command <name>",
description: 'Export schema for a specific command only (e.g. "image generate")',
},
],
examples: ["bl config export-schema", 'bl config export-schema --command "video generate"'],
async run(config: Config, flags: GlobalFlags) {
const { commands } = await import("../catalog.ts");
const targetCommand = flags.command as string | undefined;
if (targetCommand) {
const command = commands[targetCommand];
if (!command) {
throw new BailianError(`Command "${targetCommand}" not found.`, ExitCode.USAGE);
}
const schema = generateToolSchema(command);
process.stdout.write(JSON.stringify(schema, null, 2) + "\n");
return;
}
// Export all suitable commands
const allCommands = Object.values(commands);
const schemas = allCommands
.filter((c) => !SKIP_PREFIXES.some((p) => c.name.startsWith(p)))
.map((c) => generateToolSchema(c));
process.stdout.write(JSON.stringify(schemas, null, 2) + "\n");
},
});
-1
View File
@@ -1 +0,0 @@
export { commands } from "./catalog.ts";
+9 -163
View File
@@ -1,164 +1,10 @@
import { scanCommandPath, parseFlags } from "./args.ts";
import { registry } from "./registry.ts";
import {
GLOBAL_OPTIONS,
loadConfig,
resolveCredential,
trackCommandExecution,
flushTelemetry,
} from "bailian-cli-core";
import { ensureApiKey } from "./utils/ensure-key.ts";
import { setupProxyFromEnv } from "./proxy.ts";
import { handleError } from "./error-handler.ts";
import {
checkForUpdate,
getPendingUpdateNotification,
performAutoUpdate,
shouldAutoUpdate,
} from "./utils/update-checker.ts";
import { maybeShowStatusBar } from "./output/status-bar.ts";
import { printWelcomeBanner, printQuickStart } from "./output/banner.ts";
import { CLI_VERSION } from "./version.ts";
import {
printCurrentCommandHelp,
registerCommandHelpPrinter,
setExecutingCommandPath,
} from "./utils/command-help.ts";
import { createCli } from "bailian-cli-runtime";
import { commands } from "./commands.ts";
import pkg from "../package.json" with { type: "json" };
// 必须在任何 fetch 发起前安装(含 update-checker / telemetry)
try {
setupProxyFromEnv();
} catch (err) {
handleError(err);
}
registerCommandHelpPrinter((commandPath, out) => {
registry.printHelp(commandPath, out);
});
// 优雅处理 Ctrl+C
// 退出前尝试 best-effort 刷出埋点,让去抖队列中 / 在途的 fetch 请求有机会
// 落网络;flush 与较短超时 race,保证 SIGINT 仍然响应及时。
process.on("SIGINT", () => {
process.stderr.write("\nInterrupted. Exiting.\n");
void flushTelemetry(500).finally(() => process.exit(130));
});
// 优雅处理 stdout EPIPE(例如管道到提前退出的 `mpv`)
process.stdout.on("error", (e: NodeJS.ErrnoException) => {
if (e.code === "EPIPE") process.exit(0);
else throw e;
});
async function main() {
let argv = process.argv.slice(2);
if (argv[0] === "--") argv = argv.slice(1);
if (argv.includes("--version") || argv.includes("-v")) {
process.stdout.write(`bl ${CLI_VERSION}\n`);
process.exit(0);
}
const commandPath = scanCommandPath(argv, GLOBAL_OPTIONS);
if (argv.includes("--help") || argv.includes("-h")) {
registry.printHelp(commandPath, process.stderr);
process.exit(0);
}
// 未传任何命令:展示帮助信息与登录引导
if (commandPath.length === 0) {
registry.printHelp([], process.stderr);
const flags = parseFlags(argv, GLOBAL_OPTIONS);
const config = loadConfig(flags);
config.clientName = "bailian-cli";
config.clientVersion = CLI_VERSION;
const hasKey = !!(
config.apiKey ||
config.fileApiKey ||
config.fileAccessToken ||
config.accessTokenEnv
);
if (hasKey) printQuickStart();
else printWelcomeBanner();
process.exit(0);
}
// 组路径(例如 `bl speech` 未接子命令):展示帮助后干净退出
if (registry.isGroupPath(commandPath)) {
registry.printHelp(commandPath, process.stderr);
process.exit(0);
}
const { command, extra } = registry.resolve(commandPath);
const flags = parseFlags(argv, [...GLOBAL_OPTIONS, ...(command.options ?? [])]);
if (extra.length > 0) (flags as Record<string, unknown>)._positional = extra;
const config = loadConfig(flags);
config.clientName = "bailian-cli";
config.clientVersion = CLI_VERSION;
// 默认执行 ensureApiKey;自行处理鉴权或仅需 Console/AK-SK 等的命令在 defineCommand 上设 skipDefaultApiKeySetup
if (!command.skipDefaultApiKeySetup) {
await ensureApiKey(config);
try {
const credential = await resolveCredential(config);
maybeShowStatusBar(config, credential.token, credential);
} catch {
/* 没有凭证,不展示状态栏 */
}
}
const updateCheckPromise = checkForUpdate(CLI_VERSION).catch(() => {});
setExecutingCommandPath(commandPath);
if (
commandPath[0] === "auth" &&
commandPath[1] === "login" &&
!flags.console &&
!String((flags.apiKey as string | undefined) ?? "").trim() &&
!String(config.apiKey ?? "").trim() &&
!process.env.DASHSCOPE_API_KEY?.trim()
) {
printCurrentCommandHelp(process.stderr);
process.exit(0);
}
await trackCommandExecution(config, commandPath, flags, () => command.execute(config, flags));
await updateCheckPromise;
const isUpdateCommand = commandPath.length === 1 && commandPath[0] === "update";
const newVersion = getPendingUpdateNotification();
if (newVersion && !config.quiet && !isUpdateCommand) {
if (shouldAutoUpdate(newVersion, CLI_VERSION)) {
// 大版本差距且目标为稳定版,自动更新
await performAutoUpdate(CLI_VERSION, newVersion);
} else {
// 普通小版本提示
const isTTY = process.stderr.isTTY;
const yellow = isTTY ? "\x1b[33m" : "";
const cyan = isTTY ? "\x1b[36m" : "";
const reset = isTTY ? "\x1b[0m" : "";
process.stderr.write(
`\n ${yellow}Update available: ${CLI_VERSION} → ${newVersion}${reset}\n`,
);
process.stderr.write(` Run ${cyan}bl update${reset} to upgrade\n\n`);
}
}
// 进程退出前尽力等待在途的埋点完成。
// 使用较短超时兜底,避免慢网拖慢用户感知。
await flushTelemetry(1000);
}
main().catch((err) => {
// 在 handleError() 调用 process.exit() 之前刷出在途埋点。
// 命令抛出的错误已被 trackCommandExecution 的 finally 块记录,
// 但底层 tracker 有 ~500ms 的发送去抖。不主动 flush 的话,
// 错误事件会随进程退出丢掉。
void flushTelemetry(1000).finally(() => handleError(err));
});
void createCli(commands, {
binName: "bl",
version: pkg.version,
clientName: "bailian-cli",
npmPackage: "bailian-cli",
}).run();
-48
View File
@@ -25,12 +25,6 @@ describe("e2e: config", () => {
expect(stderr).toMatch(/set|--key|--value/i);
});
test("config export-schema --help 正常退出", async () => {
const { stderr, exitCode } = await runCli(["config", "export-schema", "--help"]);
expect(exitCode, stderr).toBe(0);
expect(stderr).toMatch(/export-schema|--command/i);
});
test("config show --output json", async () => {
const { stdout, stderr, exitCode } = await runCli([
"config",
@@ -146,46 +140,4 @@ describe("e2e: config", () => {
const data = parseStdoutJson<{ would_set?: { default_text_model?: string } }>(stdout);
expect(data.would_set?.default_text_model).toBe("qwen3.7-max");
});
test("config export-schema --command 导出单条工具 JSON", async () => {
const { stdout, stderr, exitCode } = await runCli([
"config",
"export-schema",
"--command",
"text chat",
"--non-interactive",
]);
expect(exitCode, stderr).toBe(0);
const schema = parseStdoutJson<{ name?: string; input_schema?: { type?: string } }>(stdout);
expect(schema.name).toMatch(/bailian_text_chat/);
expect(schema.input_schema?.type).toBe("object");
});
test("config export-schema 不存在的子命令时报错", async () => {
const { stderr, exitCode } = await runCli([
"config",
"export-schema",
"--command",
"this-command-does-not-exist-xyz",
"--non-interactive",
"--output",
"json",
]);
expect(exitCode).toBe(2);
const err = JSON.parse(stderr.trim()) as { error?: { message?: string } };
expect(err.error?.message).toMatch(/not found/i);
});
test("config export-schema 导出全部为 JSON 数组", async () => {
const { stdout, stderr, exitCode } = await runCli([
"config",
"export-schema",
"--non-interactive",
]);
expect(exitCode, stderr).toBe(0);
const arr = parseStdoutJson<Array<{ name?: string }>>(stdout);
expect(Array.isArray(arr)).toBe(true);
expect(arr.length).toBeGreaterThan(0);
expect(arr[0]?.name).toMatch(/^bailian_/);
});
});
+1 -1
View File
@@ -28,7 +28,7 @@ const FAKE_URL = `https://${FAKE_HOST}/probe`;
* 代理行为由进程环境变量决定,正是被测对象;fetch 成败不重要,我们只看代理是否收到 CONNECT。
*/
const PROBE_SCRIPT = `
import { setupProxyFromEnv } from ${JSON.stringify(join(cliPackageRoot, "src", "proxy.ts"))};
import { setupProxyFromEnv } from ${JSON.stringify(join(cliPackageRoot, "..", "runtime", "src", "proxy.ts"))};
setupProxyFromEnv();
try {
await fetch(${JSON.stringify(FAKE_URL)}, { signal: AbortSignal.timeout(5000) });
+1
View File
@@ -5,6 +5,7 @@
"moduleDetection": "force",
"module": "nodenext",
"moduleResolution": "nodenext",
"customConditions": ["@bailian-cli/source"],
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
+4
View File
@@ -0,0 +1,4 @@
node_modules
dist
*.log
.DS_Store
+58
View File
@@ -0,0 +1,58 @@
{
"name": "bailian-cli-commands",
"version": "1.5.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": {
"url": "https://github.com/modelstudioai/cli/issues"
},
"license": "Apache-2.0",
"author": "Aliyun Model Studio",
"repository": {
"type": "git",
"url": "git+https://github.com/modelstudioai/cli.git",
"directory": "packages/commands"
},
"files": [
"dist"
],
"type": "module",
"types": "./dist/index.d.mts",
"exports": {
".": {
"@bailian-cli/source": "./src/index.ts",
"default": "./dist/index.mjs"
},
"./package.json": "./package.json"
},
"publishConfig": {
"access": "public",
"exports": {
".": "./dist/index.mjs",
"./package.json": "./package.json"
},
"registry": "https://registry.npmjs.org/"
},
"scripts": {
"build": "vp pack",
"dev": "vp pack --watch",
"test": "vp test",
"check": "vp check"
},
"dependencies": {
"bailian-cli-core": "workspace:*",
"bailian-cli-runtime": "workspace:*",
"boxen": "catalog:",
"chalk": "catalog:",
"yaml": "catalog:"
},
"devDependencies": {
"@types/node": "catalog:",
"@typescript/native-preview": "7.0.0-dev.20260328.1",
"typescript": "^6.0.2",
"vite-plus": "0.1.22"
},
"engines": {
"node": ">=22.12.0"
}
}
@@ -17,9 +17,9 @@ import {
} from "bailian-cli-core";
import boxen from "boxen";
import chalk, { Chalk, type ChalkInstance } from "chalk";
import { emitBare, emitResult } from "../../output/output.ts";
import { createSpinner } from "../../output/progress.ts";
import { failIfMissing, promptText } from "../../output/prompt.ts";
import { emitBare, emitResult } from "bailian-cli-runtime";
import { createSpinner } from "bailian-cli-runtime";
import { failIfMissing, promptText, cmdUsage } from "bailian-cli-runtime";
function formatContextWindow(tokens: number): string {
if (tokens >= 1_000_000)
@@ -215,10 +215,9 @@ function isEmptyResult(result: RecommendResult): boolean {
}
export default defineCommand({
name: "advisor recommend",
description:
"Recommend the best models for your use case (intent analysis → candidate recall → LLM ranking)",
usage: "bl advisor recommend <prompt> [flags]",
usageArgs: "<prompt> [flags]",
options: [
{
flag: "--message <text>",
@@ -233,13 +232,13 @@ export default defineCommand({
description: "Output format: text (default in TTY), json, yaml",
},
],
examples: [
'bl advisor recommend --message "I need a visual-understanding chatbot"',
'bl advisor recommend --message "Build an Agent that auto-generates animations"',
'bl advisor recommend --message "Legal contract review, high precision required"',
'bl advisor recommend --message "Low-cost high-concurrency online customer service" --output json',
'bl advisor recommend --message "Long document summarization" --dry-run',
"bl advisor recommend # Interactive input",
exampleArgs: [
'--message "I need a visual-understanding chatbot"',
'--message "Build an Agent that auto-generates animations"',
'--message "Legal contract review, high precision required"',
'--message "Low-cost high-concurrency online customer service" --output json',
'--message "Long document summarization" --dry-run',
" # Interactive input",
],
async run(config: Config, flags: GlobalFlags) {
const positional = ((flags as Record<string, unknown>)._positional as string[]) ?? [];
@@ -254,7 +253,7 @@ export default defineCommand({
}
userInput = hint;
} else {
failIfMissing("message", 'bl advisor recommend "your requirement"');
failIfMissing("message", cmdUsage(config, '"your requirement"'));
}
}
@@ -11,13 +11,12 @@ import {
type AppStreamChunk,
type AppCompletionResponse,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "app call",
description: "Call a Bailian application (agent or workflow)",
usage: "bl app call --app-id <id> --prompt <text> [flags]",
usageArgs: "--app-id <id> --prompt <text> [flags]",
options: [
{ flag: "--app-id <id>", description: "Application ID (required)", required: true },
{ flag: "--prompt <text>", description: "Input prompt text", required: true },
@@ -34,20 +33,20 @@ export default defineCommand({
{ flag: "--biz-params <json>", description: "Business parameters JSON (workflow variables)" },
{ flag: "--has-thoughts", description: "Show agent thinking process" },
],
examples: [
'bl app call --app-id abc123 --prompt "Hello"',
'bl app call --app-id abc123 --prompt "Describe this image" --image https://example.com/photo.jpg',
'bl app call --app-id abc123 --prompt "Analyze the image" --image img1.jpg --image img2.jpg',
'bl app call --app-id abc123 --prompt "Continue" --session-id sess_xxx --stream',
'bl app call --app-id abc123 --prompt "Search for materials" --pipeline-ids pipe1,pipe2',
'bl app call --app-id abc123 --prompt "Start" --biz-params \'{"key":"value"}\'',
exampleArgs: [
'--app-id abc123 --prompt "Hello"',
'--app-id abc123 --prompt "Describe this image" --image https://example.com/photo.jpg',
'--app-id abc123 --prompt "Analyze the image" --image img1.jpg --image img2.jpg',
'--app-id abc123 --prompt "Continue" --session-id sess_xxx --stream',
'--app-id abc123 --prompt "Search for materials" --pipeline-ids pipe1,pipe2',
'--app-id abc123 --prompt "Start" --biz-params \'{"key":"value"}\'',
],
async run(config: Config, flags: GlobalFlags) {
const appId = flags.appId as string;
if (!appId) failIfMissing("app-id", "bl app call --app-id <id> --prompt <text>");
if (!appId) failIfMissing("app-id", cmdUsage(config, "--app-id <id> --prompt <text>"));
const prompt = flags.prompt as string;
if (!prompt) failIfMissing("prompt", "bl app call --app-id <id> --prompt <text>");
if (!prompt) failIfMissing("prompt", cmdUsage(config, "--app-id <id> --prompt <text>"));
const shouldStream =
flags.stream === true || (flags.stream === undefined && process.stdout.isTTY);
@@ -6,15 +6,14 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { emitResult } from "../../output/output.ts";
import { emitResult } from "bailian-cli-runtime";
const APP_LIST_API = "zeldaEasy.broadscope-bailian.app-control.list";
export default defineCommand({
name: "app list",
description: "List Bailian applications",
skipDefaultApiKeySetup: true,
usage: "bl app list [flags]",
usageArgs: "[flags]",
options: [
{
flag: "--name <name>",
@@ -41,12 +40,7 @@ export default defineCommand({
type: "number",
},
],
examples: [
"bl app list",
"bl app list --name customer service",
"bl app list --page 2 --page-size 10",
"bl app list --output json",
],
exampleArgs: ["", "--name customer service", "--page 2 --page-size 10", "--output json"],
async run(config: Config, flags: GlobalFlags) {
const name = (flags.name as string) || "";
const pageNo = (flags.page as number) || 1;
@@ -7,10 +7,10 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { printQuickStart } from "../../output/banner.ts";
import { emitBare } from "../../output/output.ts";
import { promptConfirm } from "../../output/prompt.ts";
import { printCurrentCommandHelp } from "../../utils/command-help.ts";
import { printQuickStart } from "bailian-cli-runtime";
import { emitBare } from "bailian-cli-runtime";
import { promptConfirm } from "bailian-cli-runtime";
import { printCurrentCommandHelp } from "bailian-cli-runtime";
import {
resolveConsoleOrigin,
runConsoleLogin,
@@ -18,10 +18,9 @@ import {
} from "./login-console.ts";
export default defineCommand({
name: "auth login",
description: "Authenticate with API key or console browser login (credentials can coexist)",
skipDefaultApiKeySetup: true,
usage: "bl auth login --api-key <key> | bl auth login --console",
usageArgs: "--api-key <key> | --console",
options: [
{ flag: "--api-key <key>", description: "DashScope API key to store" },
{
@@ -34,7 +33,7 @@ export default defineCommand({
"Sign in via browser; use --console-site to choose domestic (default) or international",
},
],
examples: ["bl auth login --api-key sk-xxxxx", "bl auth login --console"],
exampleArgs: ["--api-key sk-xxxxx", "--console"],
async run(config: Config, flags: GlobalFlags) {
if (flags.console) {
if (config.dryRun) {
@@ -7,7 +7,7 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { emitBare } from "../../output/output.ts";
import { emitBare } from "bailian-cli-runtime";
async function clearConsoleToken(): Promise<boolean> {
const file = readConfigFile() as Record<string, unknown>;
@@ -18,10 +18,9 @@ async function clearConsoleToken(): Promise<boolean> {
}
export default defineCommand({
name: "auth logout",
description: "Clear stored credentials",
skipDefaultApiKeySetup: true,
usage: "bl auth logout [--console] [--yes] [--dry-run]",
usageArgs: "[--console] [--yes] [--dry-run]",
options: [
{
flag: "--console",
@@ -30,12 +29,7 @@ export default defineCommand({
},
{ flag: "--yes", description: "Skip confirmation prompt" },
],
examples: [
"bl auth logout",
"bl auth logout --console",
"bl auth logout --dry-run",
"bl auth logout --yes",
],
exampleArgs: ["", "--console", "--dry-run", "--yes"],
async run(config: Config, flags: GlobalFlags) {
const file = readConfigFile();
@@ -8,8 +8,8 @@ import {
type GlobalFlags,
type ResolvedCredential,
} from "bailian-cli-core";
import { emitResult, emitBare } from "../../output/output.ts";
import { API_KEY_PAGE } from "../../urls.ts";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { API_KEY_PAGE } from "bailian-cli-runtime";
interface StoredCredential {
configured: boolean;
@@ -108,7 +108,7 @@ function hasAnyAuth(status: AuthStatusPayload): boolean {
);
}
function emitTextStatus(status: AuthStatusPayload): void {
function emitTextStatus(status: AuthStatusPayload, config: Config): void {
emitBare("Authentication Status:");
emitBare(" Stored credentials (can coexist):");
if (status.api_key.configured) {
@@ -134,14 +134,12 @@ function emitTextStatus(status: AuthStatusPayload): void {
` Console gateway: ${status.console_gateway_commands.method} (${status.console_gateway_commands.source}) ${status.console_gateway_commands.masked}`,
);
} else {
emitBare(" Console gateway: unavailable (run bl auth login --console)");
emitBare(` Console gateway: unavailable (run ${config.binName} auth login --console)`);
}
}
export default defineCommand({
name: "auth status",
description: "Show current authentication state",
usage: "bl auth status",
options: [
{ flag: "--console-region <region>", description: "Console region" },
{
@@ -154,7 +152,7 @@ export default defineCommand({
type: "number",
},
],
examples: ["bl auth status", "bl auth status --output json"],
exampleArgs: ["", "--output json"],
async run(config: Config, _flags: GlobalFlags) {
const format = detectOutputFormat(config.output);
const status = await buildStatus(config);
@@ -164,8 +162,8 @@ export default defineCommand({
authenticated: false,
message: "Not authenticated.",
hint: [
"DashScope API: bl auth login --api-key <key> or DASHSCOPE_API_KEY",
"Console gateway: bl auth login --console or DASHSCOPE_ACCESS_TOKEN",
`DashScope API: ${config.binName} auth login --api-key <key> or DASHSCOPE_API_KEY`,
`Console gateway: ${config.binName} auth login --console or DASHSCOPE_ACCESS_TOKEN`,
`Get API Key: ${API_KEY_PAGE}`,
].join("\n"),
...status,
@@ -179,6 +177,6 @@ export default defineCommand({
return;
}
emitTextStatus(status);
emitTextStatus(status, config);
},
});
@@ -9,7 +9,7 @@ import {
type GlobalFlags,
ExitCode,
} from "bailian-cli-core";
import { emitResult } from "../../output/output.ts";
import { emitResult, cmdUsage } from "bailian-cli-runtime";
const VALID_KEYS = [
"base_url",
@@ -50,10 +50,9 @@ const KEY_ALIASES: Record<string, string> = {
};
export default defineCommand({
name: "config set",
description: "Set a config value",
skipDefaultApiKeySetup: true,
usage: "bl config set --key <key> --value <value>",
usageArgs: "--key <key> --value <value>",
options: [
{
flag: "--key <key>",
@@ -62,10 +61,10 @@ export default defineCommand({
},
{ flag: "--value <value>", description: "Value to set" },
],
examples: [
"bl config set --key output --value json",
"bl config set --key timeout --value 600",
"bl config set --key base_url --value https://dashscope.aliyuncs.com",
exampleArgs: [
"--key output --value json",
"--key timeout --value 600",
"--key base_url --value https://dashscope.aliyuncs.com",
],
async run(config: Config, flags: GlobalFlags) {
const key = flags.key as string | undefined;
@@ -75,7 +74,7 @@ export default defineCommand({
throw new BailianError(
"--key and --value are required.",
ExitCode.USAGE,
"bl config set --key <key> --value <value>",
cmdUsage(config, "--key <key> --value <value>"),
);
}
@@ -7,14 +7,12 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { emitResult } from "../../output/output.ts";
import { emitResult } from "bailian-cli-runtime";
export default defineCommand({
name: "config show",
description: "Display current configuration",
skipDefaultApiKeySetup: true,
usage: "bl config show",
examples: ["bl config show", "bl config show --output json"],
exampleArgs: ["", "--output json"],
async run(config: Config, _flags: GlobalFlags) {
const file = loadConfigFile();
const format = detectOutputFormat(config.output);
@@ -9,14 +9,13 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult } from "../../output/output.ts";
import { failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult } from "bailian-cli-runtime";
export default defineCommand({
name: "console call",
description: "Call a Bailian console API via the CLI gateway",
skipDefaultApiKeySetup: true,
usage: "bl console call --api <api> --data <json> [flags]",
usageArgs: "--api <api> --data <json> [flags]",
options: [
{
flag: "--api <api>",
@@ -39,16 +38,16 @@ export default defineCommand({
type: "number",
},
],
examples: [
`bl console call --api zeldaEasy.broadscope-bailian.freeTrial.queryFreeTierQuota --data '{"queryFreeTierQuotaRequest":{"models":["qwen3-max"]}}'`,
`bl console call --api some.api.name --data '{"key":"value"}' --console-region cn-beijing`,
exampleArgs: [
`--api zeldaEasy.broadscope-bailian.freeTrial.queryFreeTierQuota --data '{"queryFreeTierQuotaRequest":{"models":["qwen3-max"]}}'`,
`--api some.api.name --data '{"key":"value"}' --console-region cn-beijing`,
],
async run(config: Config, flags: GlobalFlags) {
const api = flags.api as string;
if (!api) failIfMissing("api", "bl console call --api <api> --data <json>");
if (!api) failIfMissing("api", cmdUsage(config, "--api <api> --data <json>"));
const dataRaw = flags.data as string;
if (!dataRaw) failIfMissing("data", "bl console call --api <api> --data <json>");
if (!dataRaw) failIfMissing("data", cmdUsage(config, "--api <api> --data <json>"));
let data: Record<string, unknown>;
try {
@@ -8,21 +8,17 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { failIfMissing, promptConfirm } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, promptConfirm } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "dataset delete",
description: "Delete a dataset file by ID",
usage: "bl dataset delete --file-id <id> [--yes]",
usageArgs: "--file-id <id> [--yes]",
options: [
{ flag: "--file-id <id>", description: "Dataset file ID (required)", required: true },
{ flag: "--yes", description: "Skip the confirmation prompt", type: "boolean" },
],
examples: [
"bl dataset delete --file-id file-id-xxx",
"bl dataset delete --file-id file-id-xxx --yes",
],
exampleArgs: ["--file-id file-id-xxx", "--file-id file-id-xxx --yes"],
async run(config: Config, flags: GlobalFlags) {
const fileId = flags.fileId as string | undefined;
if (!fileId) failIfMissing("file-id", "bl dataset delete --file-id <id>");
@@ -5,18 +5,14 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "dataset get",
description: "Get details of a single dataset file",
usage: "bl dataset get --file-id <id>",
usageArgs: "--file-id <id>",
options: [{ flag: "--file-id <id>", description: "Dataset file ID (required)", required: true }],
examples: [
"bl dataset get --file-id file-xxx",
"bl dataset get --file-id file-xxx --output json",
],
exampleArgs: ["--file-id file-xxx", "--file-id file-xxx --output json"],
async run(config: Config, flags: GlobalFlags) {
const fileId = flags.fileId as string | undefined;
if (!fileId) failIfMissing("file-id", "bl dataset get --file-id <id>");
@@ -5,13 +5,12 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { emitResult, emitBare } from "../../output/output.ts";
import { formatTable } from "../../output/table.ts";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { formatTable } from "bailian-cli-runtime";
export default defineCommand({
name: "dataset list",
description: "List uploaded dataset files",
usage: "bl dataset list [--page <n>] [--page-size <n>] [--purpose <name>]",
usageArgs: "[--page <n>] [--page-size <n>] [--purpose <name>]",
options: [
{ flag: "--page <n>", description: "Page number (default: 1)", type: "number" },
{
@@ -24,12 +23,7 @@ export default defineCommand({
description: 'Filter by purpose (e.g. "fine-tune", "evaluation"). Omit to list all.',
},
],
examples: [
"bl dataset list",
"bl dataset list --purpose fine-tune",
"bl dataset list --purpose evaluation --page-size 20",
"bl dataset list --output json",
],
exampleArgs: ["", "--purpose fine-tune", "--purpose evaluation --page-size 20", "--output json"],
async run(config: Config, flags: GlobalFlags) {
const format = detectOutputFormat(config.output);
const pageNo = flags.page !== undefined ? (flags.page as number) : undefined;
@@ -12,14 +12,13 @@ import {
type GlobalFlags,
type DatasetFile,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "dataset upload",
description: "Upload a dataset file (.jsonl) to Bailian",
usage:
"bl dataset upload --file <path> [--purpose <name>] [--schema <chatml|dpo|cpt>] [--no-validate] [--full-validate]",
usageArgs:
"--file <path> [--purpose <name>] [--schema <chatml|dpo|cpt>] [--no-validate] [--full-validate]",
options: [
{
flag: "--file <path>",
@@ -46,13 +45,13 @@ export default defineCommand({
type: "boolean",
},
],
examples: [
"bl dataset upload --file train.jsonl",
"bl dataset upload --file dpo.jsonl --schema dpo",
"bl dataset upload --file cpt.jsonl --schema cpt",
"bl dataset upload --file eval.jsonl --purpose evaluation",
"bl dataset upload --file train.jsonl --full-validate",
"bl dataset upload --file train.jsonl --no-validate",
exampleArgs: [
"--file train.jsonl",
"--file dpo.jsonl --schema dpo",
"--file cpt.jsonl --schema cpt",
"--file eval.jsonl --purpose evaluation",
"--file train.jsonl --full-validate",
"--file train.jsonl --no-validate",
],
notes: [
"Only .jsonl is supported in this release. Three record schemas are",
@@ -10,8 +10,8 @@ import {
type GlobalFlags,
type ValidationResult,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
function formatStats(result: ValidationResult): string[] {
const out: string[] = [];
@@ -24,11 +24,10 @@ function formatStats(result: ValidationResult): string[] {
}
export default defineCommand({
name: "dataset validate",
description: "Locally validate a dataset file (.jsonl) without uploading",
// 纯本地校验,不触网、不需 API key(与 `pipeline validate` 一致)。
skipDefaultApiKeySetup: true,
usage: "bl dataset validate --file <path> [--full-validate] [--schema <chatml|dpo|cpt>]",
usageArgs: "--file <path> [--full-validate] [--schema <chatml|dpo|cpt>]",
options: [
{ flag: "--file <path>", description: "Local .jsonl dataset file", required: true },
{
@@ -42,12 +41,12 @@ export default defineCommand({
'Record schema: "chatml" (SFT), "dpo" (chosen/rejected), or "cpt" (raw text). Default auto-detects per record.',
},
],
examples: [
"bl dataset validate --file train.jsonl",
"bl dataset validate --file dpo.jsonl --schema dpo",
"bl dataset validate --file cpt.jsonl --schema cpt",
"bl dataset validate --file eval.jsonl --full-validate",
"bl dataset validate --file train.jsonl --output json",
exampleArgs: [
"--file train.jsonl",
"--file dpo.jsonl --schema dpo",
"--file cpt.jsonl --schema cpt",
"--file eval.jsonl --full-validate",
"--file train.jsonl --output json",
],
notes: [
"Default scan: every line gets a structural check, then ~160 lines (front 50,",
@@ -7,8 +7,8 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { failIfMissing, promptConfirm } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, promptConfirm } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { pickPlanStrategy } from "./plans.ts";
/**
@@ -23,10 +23,9 @@ import { pickPlanStrategy } from "./plans.ts";
* `--model` (model identifier) and `--name` (console display name) are required.
*/
export default defineCommand({
name: "deploy create",
description: "Create a model deployment",
usage:
"bl deploy create --model <model_name> --name <display_name> [--plan <plan>] [--template-id <id>] [--capacity <n>] [--billing-method <m>] [--input-tpm <n>] [--output-tpm <n>] [--thinking-output-tpm <n>] [--yes]",
usageArgs:
"--model <model_name> --name <display_name> [--plan <plan>] [--template-id <id>] [--capacity <n>] [--billing-method <m>] [--input-tpm <n>] [--output-tpm <n>] [--thinking-output-tpm <n>] [--yes]",
options: [
{
flag: "--model <name>",
@@ -73,11 +72,11 @@ export default defineCommand({
},
{ flag: "--yes", description: "Skip the confirmation prompt", type: "boolean" },
],
examples: [
"bl deploy create --model my-qwen-sft --name my-sft-test",
"bl deploy create --model qwen3.6-flash-2026-04-16 --name my-flash --plan ptu --input-tpm 10000 --output-tpm 1000",
"bl deploy create --model qwen3-8b --name my-qwen3-mu --plan mu",
"bl deploy create --model qwen3-8b --name my-qwen3 --plan mu --template-id MU1 --capacity 2 --yes",
exampleArgs: [
"--model my-qwen-sft --name my-sft-test",
"--model qwen3.6-flash-2026-04-16 --name my-flash --plan ptu --input-tpm 10000 --output-tpm 1000",
"--model qwen3-8b --name my-qwen3-mu --plan mu",
"--model qwen3-8b --name my-qwen3 --plan mu --template-id MU1 --capacity 2 --yes",
],
notes: [
"Plan defaults to `lora` (Token-billed). Pass --plan to override.",
@@ -8,8 +8,8 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { failIfMissing, promptConfirm } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, promptConfirm } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
/**
* `bl deploy delete` — destroy a deployment.
@@ -19,9 +19,8 @@ import { emitResult, emitBare } from "../../output/output.ts";
* DELETE call.
*/
export default defineCommand({
name: "deploy delete",
description: "Delete a model deployment (must be STOPPED or FAILED)",
usage: "bl deploy delete --deployed-model <id> [--yes] [--skip-precheck]",
usageArgs: "--deployed-model <id> [--yes] [--skip-precheck]",
options: [
{
flag: "--deployed-model <id>",
@@ -35,10 +34,7 @@ export default defineCommand({
type: "boolean",
},
],
examples: [
"bl deploy delete --deployed-model dep-...",
"bl deploy delete --deployed-model dep-... --yes",
],
exampleArgs: ["--deployed-model dep-...", "--deployed-model dep-... --yes"],
async run(config: Config, flags: GlobalFlags) {
const deployedModel = flags.deployedModel as string | undefined;
if (!deployedModel) failIfMissing("deployed-model", "bl deploy delete --deployed-model <id>");
@@ -5,13 +5,12 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "deploy get",
description: "Get details of a single model deployment",
usage: "bl deploy get --deployed-model <id>",
usageArgs: "--deployed-model <id>",
options: [
{
flag: "--deployed-model <id>",
@@ -19,9 +18,9 @@ export default defineCommand({
required: true,
},
],
examples: [
"bl deploy get --deployed-model qwen-plus-2025-12-01-b6d61c71",
"bl deploy get --deployed-model qwen-plus-2025-12-01-b6d61c71 --output json",
exampleArgs: [
"--deployed-model qwen-plus-2025-12-01-b6d61c71",
"--deployed-model qwen-plus-2025-12-01-b6d61c71 --output json",
],
async run(config: Config, flags: GlobalFlags) {
const deployedModel = flags.deployedModel as string | undefined;
@@ -71,7 +70,8 @@ export default defineCommand({
const label = (key: string) => `${key}:`.padEnd(18);
for (const [key, value] of Object.entries(item)) {
if (value === "" || value === undefined) continue;
emitBare(`${label(key)}${value}`);
const display = typeof value === "string" ? value : JSON.stringify(value);
emitBare(`${label(key)}${display}`);
}
},
});
@@ -5,13 +5,12 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { emitResult, emitBare } from "../../output/output.ts";
import { formatTable } from "../../output/table.ts";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { formatTable } from "bailian-cli-runtime";
export default defineCommand({
name: "deploy list",
description: "List model deployments",
usage: "bl deploy list [--page <n>] [--page-size <n>] [--status <s>]",
usageArgs: "[--page <n>] [--page-size <n>] [--status <s>]",
options: [
{ flag: "--page <n>", description: "Page number (default: 1)", type: "number" },
{
@@ -24,11 +23,7 @@ export default defineCommand({
description: "Filter by status (PENDING / RUNNING / STOPPED / FAILED)",
},
],
examples: [
"bl deploy list",
"bl deploy list --status RUNNING",
"bl deploy list --page-size 20 --output json",
],
exampleArgs: ["", "--status RUNNING", "--page-size 20 --output json"],
async run(config: Config, flags: GlobalFlags) {
const format = detectOutputFormat(config.output);
const pageNo = flags.page !== undefined ? (flags.page as number) : undefined;
@@ -5,14 +5,12 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { emitResult, emitBare } from "../../output/output.ts";
import { formatTable } from "../../output/table.ts";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { formatTable } from "bailian-cli-runtime";
export default defineCommand({
name: "deploy models",
description: "List models available for deployment",
usage:
"bl deploy models [--page <n>] [--page-size <n>] [--version <v>] [--source <custom|public>]",
usageArgs: "[--page <n>] [--page-size <n>] [--version <v>] [--source <custom|public>]",
options: [
{ flag: "--page <n>", description: "Page number (default: 1)", type: "number" },
{
@@ -29,11 +27,11 @@ export default defineCommand({
description: "Model source filter: custom (fine-tuned) | base (catalog) | public",
},
],
examples: [
"bl deploy models",
"bl deploy models --source base",
"bl deploy models --source custom --page-size 50",
"bl deploy models --version v1.0 --output json",
exampleArgs: [
"",
"--source base",
"--source custom --page-size 50",
"--version v1.0 --output json",
],
async run(config: Config, flags: GlobalFlags) {
const format = detectOutputFormat(config.output);
@@ -21,7 +21,7 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { failIfMissing } from "bailian-cli-runtime";
export interface PlanContext {
config: Config;
@@ -7,8 +7,8 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { failIfMissing, promptConfirm } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, promptConfirm } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
/**
* `bl deploy scale` — adjust capacity (and optional PTU input/output token rates).
@@ -17,10 +17,8 @@ import { emitResult, emitBare } from "../../output/output.ts";
* integer multiple of `base_capacity` (visible via `bl deploy get`).
*/
export default defineCommand({
name: "deploy scale",
description: "Scale a deployment's capacity",
usage:
"bl deploy scale --deployed-model <id> --capacity <n> [--input-tpm <n>] [--output-tpm <n>] [--yes]",
usageArgs: "--deployed-model <id> --capacity <n> [--input-tpm <n>] [--output-tpm <n>] [--yes]",
options: [
{
flag: "--deployed-model <id>",
@@ -44,9 +42,9 @@ export default defineCommand({
},
{ flag: "--yes", description: "Skip the confirmation prompt", type: "boolean" },
],
examples: [
"bl deploy scale --deployed-model qwen-plus-...-b6d61c71 --capacity 8",
"bl deploy scale --deployed-model dep-... --capacity 2 --yes",
exampleArgs: [
"--deployed-model qwen-plus-...-b6d61c71 --capacity 8",
"--deployed-model dep-... --capacity 2 --yes",
],
async run(config: Config, flags: GlobalFlags) {
const deployedModel = flags.deployedModel as string | undefined;
@@ -7,8 +7,8 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { failIfMissing, promptConfirm } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, promptConfirm } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
/**
* `bl deploy update` — update deployment rate limits.
@@ -17,9 +17,8 @@ import { emitResult, emitBare } from "../../output/output.ts";
* Body: at least one of `rpm_limit` (requests/min) or `tpm_limit` (tokens/min).
*/
export default defineCommand({
name: "deploy update",
description: "Update a deployment's rate limits (rpm_limit / tpm_limit)",
usage: "bl deploy update --deployed-model <id> [--rpm-limit <n>] [--tpm-limit <n>] [--yes]",
usageArgs: "--deployed-model <id> [--rpm-limit <n>] [--tpm-limit <n>] [--yes]",
options: [
{
flag: "--deployed-model <id>",
@@ -38,18 +37,15 @@ export default defineCommand({
},
{ flag: "--yes", description: "Skip the confirmation prompt", type: "boolean" },
],
examples: [
"bl deploy update --deployed-model dep-... --rpm-limit 1000",
"bl deploy update --deployed-model dep-... --rpm-limit 1000 --tpm-limit 200000 --yes",
exampleArgs: [
"--deployed-model dep-... --rpm-limit 1000",
"--deployed-model dep-... --rpm-limit 1000 --tpm-limit 200000 --yes",
],
notes: ["At least one of --rpm-limit / --tpm-limit must be provided."],
async run(config: Config, flags: GlobalFlags) {
const deployedModel = flags.deployedModel as string | undefined;
if (!deployedModel)
failIfMissing(
"deployed-model",
"bl deploy update --deployed-model <id> [--rpm-limit <n>] [--tpm-limit <n>]",
);
failIfMissing("deployed-model", "--deployed-model <id> [--rpm-limit <n>] [--tpm-limit <n>]");
const rpmLimit = flags.rpmLimit !== undefined ? (flags.rpmLimit as number) : undefined;
const tpmLimit = flags.tpmLimit !== undefined ? (flags.tpmLimit as number) : undefined;
@@ -6,13 +6,12 @@ import {
type GlobalFlags,
uploadFile,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "file upload",
description: "Upload a local file to DashScope temporary storage (48h)",
usage: "bl file upload --file <path> --model <model>",
usageArgs: "--file <path> --model <model>",
options: [
{
flag: "--file <path>",
@@ -25,21 +24,21 @@ export default defineCommand({
required: true,
},
],
examples: [
"bl file upload --file photo.jpg --model qwen3-vl-plus",
"bl file upload --file video.mp4 --model wan2.1-t2v-plus",
"bl file upload --file audio.wav --model qwen3-asr-flash",
"bl file upload --file cat.png --model qwen-image-2.0",
exampleArgs: [
"--file photo.jpg --model qwen3-vl-plus",
"--file video.mp4 --model wan2.1-t2v-plus",
"--file audio.wav --model qwen3-asr-flash",
"--file cat.png --model qwen-image-2.0",
],
async run(config: Config, flags: GlobalFlags) {
const filePath = flags.file as string | undefined;
if (!filePath) {
failIfMissing("file", "bl file upload --file <path> --model <model>");
failIfMissing("file", cmdUsage(config, "--file <path> --model <model>"));
}
const model = flags.model as string | undefined;
if (!model) {
failIfMissing("model", "bl file upload --file <path> --model <model>");
failIfMissing("model", cmdUsage(config, "--file <path> --model <model>"));
}
const format = detectOutputFormat(config.output);
@@ -7,18 +7,17 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { failIfMissing, promptConfirm } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, promptConfirm } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "finetune cancel",
description: "Cancel a running fine-tune job",
usage: "bl finetune cancel --job-id <id> [--yes]",
usageArgs: "--job-id <id> [--yes]",
options: [
{ flag: "--job-id <id>", description: "Fine-tune job ID (required)", required: true },
{ flag: "--yes", description: "Skip the confirmation prompt", type: "boolean" },
],
examples: ["bl finetune cancel --job-id ft-xxx", "bl finetune cancel --job-id ft-xxx --yes"],
exampleArgs: ["bl finetune cancel --job-id ft-xxx", "bl finetune cancel --job-id ft-xxx --yes"],
notes: [
"Only PENDING / RUNNING jobs can be cancelled. Completed / failed / already-",
"cancelled jobs return a server-side error (passed through verbatim).",
@@ -12,8 +12,8 @@ import {
type GlobalFlags,
type ModelCapability,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
const PAGE_SIZE = 50;
@@ -45,10 +45,9 @@ function describeTrainingType(value: string): string {
}
export default defineCommand({
name: "finetune capability",
description:
"Query fine-tune training capability — by model (which training types it supports) or by training type (which models support it)",
usage: "bl finetune capability --model <m> | --training-type <t>",
usageArgs: "--model <m> | --training-type <t>",
options: [
{
flag: "--model <m>",
@@ -59,11 +58,11 @@ export default defineCommand({
description: `List models supporting this training type: ${TRAINING_TYPES_CLI.join(" | ")}.`,
},
],
examples: [
"bl finetune capability --model qwen3-8b",
"bl finetune capability --training-type sft-lora",
"bl finetune capability --training-type cpt --output json",
"bl finetune capability --training-type sft --quiet",
exampleArgs: [
"--model qwen3-8b",
"--training-type sft-lora",
"--training-type cpt --output json",
"--training-type sft --quiet",
],
notes: [
"Exactly one of --model / --training-type is required.",
@@ -79,10 +78,7 @@ export default defineCommand({
throw new Error("--model and --training-type are mutually exclusive; pass one.");
}
if (!model && !trainingType) {
failIfMissing(
"model or training-type",
"bl finetune capability --model <m> | --training-type <t>",
);
failIfMissing("model or training-type", "--model <m> | --training-type <t>");
}
const format = detectOutputFormat(config.output);
@@ -5,19 +5,15 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { formatTable } from "../../output/table.ts";
import { failIfMissing } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { formatTable } from "bailian-cli-runtime";
export default defineCommand({
name: "finetune checkpoints",
description: "List checkpoints produced by a fine-tune job",
usage: "bl finetune checkpoints --job-id <id>",
usageArgs: "--job-id <id>",
options: [{ flag: "--job-id <id>", description: "Fine-tune job ID (required)", required: true }],
examples: [
"bl finetune checkpoints --job-id ft-xxx",
"bl finetune checkpoints --job-id ft-xxx --output json",
],
exampleArgs: ["--job-id ft-xxx", "--job-id ft-xxx --output json"],
notes: [
"Use the returned `checkpoint` value with `bl finetune export` to publish",
"a deployable model.",
@@ -24,8 +24,8 @@ import {
} from "bailian-cli-core";
import { existsSync, statSync } from "fs";
import { basename } from "path";
import { failIfMissing, promptConfirm } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, promptConfirm } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
/**
* A `--datasets` / `--validations` token is treated as a local file to upload
@@ -194,10 +194,9 @@ async function uploadResolvedLocal(
}
export default defineCommand({
name: "finetune create",
description: "Create a fine-tune job (sft | sft-lora | dpo | dpo-lora | cpt)",
usage:
"bl finetune create --model <model> --datasets <id|path,...> [--validations <id|path,...>] [--model-name <name>] [--suffix <text>] [--n-epochs <n>] [--batch-size <n>] [--learning-rate <str>] [--max-length <n>] [--training-type <sft|sft-lora|dpo|dpo-lora|cpt>] [--yes]",
usageArgs:
"--model <model> --datasets <id|path,...> [--validations <id|path,...>] [--model-name <name>] [--suffix <text>] [--n-epochs <n>] [--batch-size <n>] [--learning-rate <str>] [--max-length <n>] [--training-type <sft|sft-lora|dpo|dpo-lora|cpt>] [--yes]",
options: [
{
flag: "--model <model>",
@@ -253,14 +252,14 @@ export default defineCommand({
type: "boolean",
},
],
examples: [
"bl finetune create --model qwen3-8b --datasets file-xxx",
"bl finetune create --model qwen3-8b --datasets ./train.jsonl",
"bl finetune create --model qwen3-8b --datasets ./train.jsonl --validations ./eval.jsonl",
"bl finetune create --model qwen3-8b --datasets file-aaa,./extra.jsonl",
"bl finetune create --model qwen3-8b --datasets ./train.jsonl --training-type sft",
exampleArgs: [
"--model qwen3-8b --datasets file-xxx",
"--model qwen3-8b --datasets ./train.jsonl",
"--model qwen3-8b --datasets ./train.jsonl --validations ./eval.jsonl",
"--model qwen3-8b --datasets file-aaa,./extra.jsonl",
"--model qwen3-8b --datasets ./train.jsonl --training-type sft",
'bl finetune create --model qwen3-8b --datasets file-xxx --learning-rate "1.6e-5" --n-epochs 4',
"bl finetune create --model qwen3-8b --datasets file-xxx --yes --output json",
"--model qwen3-8b --datasets file-xxx --yes --output json",
],
notes: [
"Training-type values use the `<method>` / `<method>-lora` convention:",
@@ -7,18 +7,17 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { failIfMissing, promptConfirm } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, promptConfirm } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "finetune delete",
description: "Delete a fine-tune job record",
usage: "bl finetune delete --job-id <id> [--yes]",
usageArgs: "--job-id <id> [--yes]",
options: [
{ flag: "--job-id <id>", description: "Fine-tune job ID (required)", required: true },
{ flag: "--yes", description: "Skip the confirmation prompt", type: "boolean" },
],
examples: ["bl finetune delete --job-id ft-xxx", "bl finetune delete --job-id ft-xxx --yes"],
exampleArgs: ["bl finetune delete --job-id ft-xxx", "bl finetune delete --job-id ft-xxx --yes"],
notes: [
"Cancel a RUNNING job first via `bl finetune cancel` — the platform refuses",
"to delete jobs that are still in flight.",
@@ -5,13 +5,12 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "finetune export",
description: "Publish a checkpoint as a deployable model",
usage: "bl finetune export --job-id <id> --checkpoint <name> --model-name <name>",
usageArgs: "--job-id <id> --checkpoint <name> --model-name <name>",
options: [
{ flag: "--job-id <id>", description: "Fine-tune job ID (required)", required: true },
{
@@ -25,7 +24,7 @@ export default defineCommand({
required: true,
},
],
examples: ["bl finetune export --job-id ft-xxx --checkpoint ckpt-3 --model-name my-qwen-sft"],
exampleArgs: ["bl finetune export --job-id ft-xxx --checkpoint ckpt-3 --model-name my-qwen-sft"],
notes: [
"Required before `bl deploy create` can target a checkpoint. The platform",
"may auto-export the best checkpoint when a job reaches SUCCEEDED — explicit",
@@ -5,15 +5,14 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "finetune get",
description: "Get details of a single fine-tune job",
usage: "bl finetune get --job-id <id>",
usageArgs: "--job-id <id>",
options: [{ flag: "--job-id <id>", description: "Fine-tune job ID (required)", required: true }],
examples: ["bl finetune get --job-id ft-xxx", "bl finetune get --job-id ft-xxx --output json"],
exampleArgs: ["bl finetune get --job-id ft-xxx", "bl finetune get --job-id ft-xxx --output json"],
async run(config: Config, flags: GlobalFlags) {
const jobId = flags.jobId as string | undefined;
if (!jobId) failIfMissing("job-id", "bl finetune get --job-id <id>");
@@ -5,13 +5,12 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { emitResult, emitBare } from "../../output/output.ts";
import { formatTable } from "../../output/table.ts";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { formatTable } from "bailian-cli-runtime";
export default defineCommand({
name: "finetune list",
description: "List fine-tune jobs",
usage: "bl finetune list [--page <n>] [--page-size <n>] [--status <s>]",
usageArgs: "[--page <n>] [--page-size <n>] [--status <s>]",
options: [
{ flag: "--page <n>", description: "Page number (default: 1)", type: "number" },
{
@@ -24,11 +23,7 @@ export default defineCommand({
description: "Filter by status (PENDING / RUNNING / SUCCEEDED / FAILED / CANCELED)",
},
],
examples: [
"bl finetune list",
"bl finetune list --status RUNNING",
"bl finetune list --page-size 20 --output json",
],
exampleArgs: ["", "--status RUNNING", "--page-size 20 --output json"],
async run(config: Config, flags: GlobalFlags) {
const format = detectOutputFormat(config.output);
const pageNo = flags.page !== undefined ? (flags.page as number) : undefined;
@@ -6,8 +6,8 @@ import {
type GlobalFlags,
type FineTuneLogEntry,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
/**
* Render a single log entry as a single line (mirrors the flatten logic used
@@ -65,10 +65,8 @@ async function fetchAllLogs(
}
export default defineCommand({
name: "finetune logs",
description: "Fetch training logs for a fine-tune job",
usage:
"bl finetune logs --job-id <id> [--page <n>] [--page-size <n>] [--search <keyword>] [--tail <n>]",
usageArgs: "--job-id <id> [--page <n>] [--page-size <n>] [--search <keyword>] [--tail <n>]",
options: [
{ flag: "--job-id <id>", description: "Fine-tune job ID (required)", required: true },
{ flag: "--page <n>", description: "Page number (default: 1)", type: "number" },
@@ -89,13 +87,13 @@ export default defineCommand({
type: "number",
},
],
examples: [
"bl finetune logs --job-id ft-xxx",
"bl finetune logs --job-id ft-xxx --page-size 100 --output json",
"bl finetune logs --job-id ft-xxx --search checkpoint",
"bl finetune logs --job-id ft-xxx --search error --output json",
"bl finetune logs --job-id ft-xxx --tail 20",
"bl finetune logs --job-id ft-xxx --search checkpoint --tail 5",
exampleArgs: [
"--job-id ft-xxx",
"--job-id ft-xxx --page-size 100 --output json",
"--job-id ft-xxx --search checkpoint",
"--job-id ft-xxx --search error --output json",
"--job-id ft-xxx --tail 20",
"--job-id ft-xxx --search checkpoint --tail 5",
],
async run(config: Config, flags: GlobalFlags) {
const jobId = flags.jobId as string | undefined;
@@ -5,8 +5,8 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
const DEFAULT_INTERVAL_SEC = 10;
const MIN_INTERVAL_SEC = 1;
@@ -67,10 +67,9 @@ function sleep(milliseconds: number, signal: AbortSignal): Promise<void> {
}
export default defineCommand({
name: "finetune watch",
description:
"Probe a fine-tune job's status (default: single non-blocking fetch). Pass --follow to poll until terminal.",
usage: "bl finetune watch --job-id <id> [--follow] [--interval <sec>] [--timeout <sec>]",
usageArgs: "--job-id <id> [--follow] [--interval <sec>] [--timeout <sec>]",
options: [
{ flag: "--job-id <id>", description: "Fine-tune job ID (required)", required: true },
{
@@ -91,12 +90,12 @@ export default defineCommand({
type: "number",
},
],
examples: [
"bl finetune watch --job-id ft-xxx # single probe, returns immediately",
"bl finetune watch --job-id ft-xxx --output json # status probe for agents",
"bl finetune watch --job-id ft-xxx --follow # block until terminal",
"bl finetune watch --job-id ft-xxx --follow --interval 5",
"bl finetune watch --job-id ft-xxx --follow --timeout 3600",
exampleArgs: [
"--job-id ft-xxx # single probe, returns immediately",
"--job-id ft-xxx --output json # status probe for agents",
"--job-id ft-xxx --follow # block until terminal",
"--job-id ft-xxx --follow --interval 5",
"--job-id ft-xxx --follow --timeout 3600",
],
notes: [
"Default (no --follow) is a NON-BLOCKING single status probe: one fetch, then",
@@ -18,21 +18,17 @@ import {
resolveBooleanFlag,
resolveWatermark,
} from "bailian-cli-core";
import { downloadFile } from "../../utils/download.ts";
import { runConcurrent, downloadParallel, getConcurrency } from "../../utils/concurrent.ts";
import { promptText, failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { resolveImageSize } from "../../utils/image-size.ts";
import { downloadFile } from "bailian-cli-runtime";
import { runConcurrent, downloadParallel, getConcurrency } from "bailian-cli-runtime";
import { promptText, failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { resolveImageSize } from "bailian-cli-runtime";
import { join } from "path";
import {
BOOL_FLAG_PROMPT_EXTEND_CLI_TRUE,
BOOL_FLAG_WATERMARK,
} from "../../utils/flag-descriptions.ts";
import { BOOL_FLAG_PROMPT_EXTEND_CLI_TRUE, BOOL_FLAG_WATERMARK } from "bailian-cli-runtime";
export default defineCommand({
name: "image edit",
description: "Edit an existing image with text instructions (Qwen-Image)",
usage: "bl image edit --image <url> --prompt <text> [flags]",
usageArgs: "--image <url> --prompt <text> [flags]",
options: [
{
flag: "--image <url>",
@@ -63,12 +59,12 @@ export default defineCommand({
{ flag: "--out-dir <dir>", description: "Download images to directory" },
{ flag: "--out-prefix <prefix>", description: "Filename prefix (default: edited)" },
],
examples: [
'bl image edit --image ./photo.png --prompt "Replace the background with a beach"',
'bl image edit --image https://example.com/logo.png --prompt "Change color to blue" --n 3',
'bl image edit --image ./a.png --image ./b.png --prompt "Merge two images into one collage"',
'bl image edit --image https://example.com/photo.png --prompt "Remove the person" --model qwen-image-2.0-pro',
'bl image edit --image ./photo.png --prompt "Replace the background with a beach" --watermark false',
exampleArgs: [
'--image ./photo.png --prompt "Replace the background with a beach"',
'--image https://example.com/logo.png --prompt "Change color to blue" --n 3',
'--image ./a.png --image ./b.png --prompt "Merge two images into one collage"',
'--image https://example.com/photo.png --prompt "Remove the person" --model qwen-image-2.0-pro',
'--image ./photo.png --prompt "Replace the background with a beach" --watermark false',
],
async run(config: Config, flags: GlobalFlags) {
// Normalize --image to string array (supports both single and repeated flags)
@@ -79,7 +75,7 @@ export default defineCommand({
rawImages = [flags.image];
}
if (rawImages.length === 0) {
failIfMissing("image", "bl image edit --image <url> --prompt <text>");
failIfMissing("image", cmdUsage(config, "--image <url> --prompt <text>"));
}
let prompt = flags.prompt as string | undefined;
@@ -94,7 +90,7 @@ export default defineCommand({
}
prompt = hint;
} else {
failIfMissing("prompt", "bl image edit --image <url> --prompt <text>");
failIfMissing("prompt", cmdUsage(config, "--image <url> --prompt <text>"));
}
}
@@ -20,16 +20,13 @@ import {
resolveBooleanFlag,
resolveWatermark,
} from "bailian-cli-core";
import { poll } from "../../utils/polling.ts";
import { downloadFile } from "../../utils/download.ts";
import { runConcurrent, downloadParallel, getConcurrency } from "../../utils/concurrent.ts";
import { promptText, failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { resolveImageSize } from "../../utils/image-size.ts";
import {
BOOL_FLAG_PROMPT_EXTEND_IMAGE_GENERATE,
BOOL_FLAG_WATERMARK,
} from "../../utils/flag-descriptions.ts";
import { poll } from "bailian-cli-runtime";
import { downloadFile } from "bailian-cli-runtime";
import { runConcurrent, downloadParallel, getConcurrency } from "bailian-cli-runtime";
import { promptText, failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { resolveImageSize } from "bailian-cli-runtime";
import { BOOL_FLAG_PROMPT_EXTEND_IMAGE_GENERATE, BOOL_FLAG_WATERMARK } from "bailian-cli-runtime";
import { join } from "path";
@@ -41,9 +38,8 @@ function isSyncModel(model: string): boolean {
}
export default defineCommand({
name: "image generate",
description: "Generate images (Qwen-Image / wan2.x)",
usage: "bl image generate --prompt <text> [flags]",
usageArgs: "--prompt <text> [flags]",
options: [
{ flag: "--prompt <text>", description: "Image description", required: true },
{ flag: "--model <model>", description: "Model ID (default: qwen-image-2.0)" },
@@ -81,16 +77,16 @@ export default defineCommand({
type: "number",
},
],
examples: [
'bl image generate --prompt "A cat in a spacesuit on Mars"',
'bl image generate --prompt "Logo design" --n 3 --out-dir ./generated/',
'bl image generate --prompt "Mountain landscape" --size 2688*1536',
'bl image generate --prompt "A castle" --seed 42 --prompt-extend false',
'bl image generate --prompt "Logo" --watermark false',
'bl image generate --prompt "An alien in the space" --watermark false',
'bl image generate --prompt "sunset" --model wan2.6-t2i --no-wait --quiet',
'bl image generate --prompt "Pro quality" --model qwen-image-2.0-pro',
'bl image generate --prompt "Product shots" --n 2 --concurrent 3 # 6 images in parallel',
exampleArgs: [
'--prompt "A cat in a spacesuit on Mars"',
'--prompt "Logo design" --n 3 --out-dir ./generated/',
'--prompt "Mountain landscape" --size 2688*1536',
'--prompt "A castle" --seed 42 --prompt-extend false',
'--prompt "Logo" --watermark false',
'--prompt "An alien in the space" --watermark false',
'--prompt "sunset" --model wan2.6-t2i --no-wait --quiet',
'--prompt "Pro quality" --model qwen-image-2.0-pro',
'--prompt "Product shots" --n 2 --concurrent 3 # 6 images in parallel',
],
async run(config: Config, flags: GlobalFlags) {
let prompt = (flags.prompt ?? (flags._positional as string[] | undefined)?.[0]) as
@@ -108,7 +104,7 @@ export default defineCommand({
}
prompt = hint;
} else {
failIfMissing("prompt", "bl image generate --prompt <text>");
failIfMissing("prompt", cmdUsage(config, "--prompt <text>"));
}
}
@@ -17,16 +17,15 @@ import {
BailianError,
ExitCode,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
const BAILIAN_HOST = "bailian.cn-beijing.aliyuncs.com";
export default defineCommand({
name: "knowledge retrieve",
description: "Retrieve from a Bailian knowledge base",
skipDefaultApiKeySetup: true,
usage: "bl knowledge retrieve --index-id <id> --query <text> [flags]",
usageArgs: "--index-id <id> --query <text> [flags]",
options: [
{ flag: "--index-id <id>", description: "Knowledge base index ID (required)", required: true },
{ flag: "--query <text>", description: "Search query (required)", required: true },
@@ -76,16 +75,16 @@ export default defineCommand({
"Authentication: pass `--api-key <key>`. AK/SK auth is deprecated and will be removed in a future version.",
"`--workspace-id` is NOT required when using --api-key.",
],
examples: [
'bl knowledge retrieve --index-id idx_xxx --query "How to use Alibaba Cloud Bailian"',
'bl knowledge retrieve --api-key $DASHSCOPE_API_KEY --index-id idx_xxx --query "RAG retrieval" --rerank --rerank-model qwen3-rerank-hybrid',
exampleArgs: [
'--index-id idx_xxx --query "How to use Alibaba Cloud Bailian"',
'--api-key $DASHSCOPE_API_KEY --index-id idx_xxx --query "RAG retrieval" --rerank --rerank-model qwen3-rerank-hybrid',
],
async run(config: Config, flags: GlobalFlags) {
const indexId = flags.indexId as string;
if (!indexId) failIfMissing("index-id", "bl knowledge retrieve --index-id <id> --query <text>");
if (!indexId) failIfMissing("index-id", cmdUsage(config, "--index-id <id> --query <text>"));
const query = flags.query as string;
if (!query) failIfMissing("query", "bl knowledge retrieve --index-id <id> --query <text>");
if (!query) failIfMissing("query", cmdUsage(config, "--index-id <id> --query <text>"));
const format = detectOutputFormat(config.output);
@@ -196,7 +195,7 @@ async function runWithAkSk(
if (!workspaceId) {
throw new BailianError(
"Knowledge retrieve requires a workspace ID.\n" +
"Set via: --workspace-id flag, or env: BAILIAN_WORKSPACE_ID, or config: bl config set workspace_id <id>",
`Set via: --workspace-id flag, or env: BAILIAN_WORKSPACE_ID, or config: ${config.binName} config set workspace_id <id>`,
ExitCode.USAGE,
);
}
@@ -6,9 +6,9 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult } from "../../output/output.ts";
import { ensureApiKey } from "../../utils/ensure-key.ts";
import { failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult } from "bailian-cli-runtime";
import { ensureApiKey } from "bailian-cli-runtime";
function parseArgFlags(raw: string[]): Record<string, unknown> {
const out: Record<string, unknown> = {};
@@ -30,10 +30,9 @@ function parseArgFlags(raw: string[]): Record<string, unknown> {
}
export default defineCommand({
name: "mcp call",
description: "Call a tool on an MCP server (tools/call)",
skipDefaultApiKeySetup: true,
usage: "bl mcp call <server-code>.<tool> [--arg k=v ...] [--json '{...}'] [--url <url>]",
usageArgs: "<server-code>.<tool> [--arg k=v ...] [--json '{...}'] [--url <url>]",
options: [
{
flag: "<server-code>.<tool>",
@@ -56,16 +55,16 @@ export default defineCommand({
},
{ flag: "--url <url>", description: "Override the MCP endpoint URL (for non-Bailian servers)" },
],
examples: [
'bl mcp call market-cmapi00073529.SmartStockSelection --query "Screen consumer stocks with ROE > 15%"',
'bl mcp call market-cmapi00073529.FinQuery --json \'{"q":"Guizhou Maotai","limit":5}\'',
"bl mcp call market-cmapi00073529.SmartFundSelection --arg riskLevel=R3 --arg minScale=10",
exampleArgs: [
'market-cmapi00073529.SmartStockSelection --query "Screen consumer stocks with ROE > 15%"',
'market-cmapi00073529.FinQuery --json \'{"q":"Guizhou Maotai","limit":5}\'',
"market-cmapi00073529.SmartFundSelection --arg riskLevel=R3 --arg minScale=10",
],
async run(config: Config, flags: GlobalFlags) {
const positional =
((flags as Record<string, unknown>)._positional as string[] | undefined) ?? [];
const target = positional[0];
if (!target) failIfMissing("<server-code>.<tool>", "bl mcp call <server-code>.<tool>");
if (!target) failIfMissing("<server-code>.<tool>", cmdUsage(config, "<server-code>.<tool>"));
const dot = target!.indexOf(".");
if (dot <= 0 || dot === target!.length - 1) {
@@ -9,7 +9,7 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { emitResult } from "../../output/output.ts";
import { emitResult } from "bailian-cli-runtime";
const MCP_LIST_API = "zeldaEasy.broadscope-bailian.mcp-server.PageList";
@@ -25,10 +25,9 @@ interface ServerSummary {
}
export default defineCommand({
name: "mcp list",
description: "List MCP servers activated under your Bailian account",
skipDefaultApiKeySetup: true,
usage: "bl mcp list [flags]",
usageArgs: "[flags]",
options: [
{ flag: "--name <text>", description: "Filter by server name (substring match)" },
{
@@ -48,7 +47,7 @@ export default defineCommand({
type: "number",
},
],
examples: ["bl mcp list", "bl mcp list --name finance", "bl mcp list --output json"],
exampleArgs: ["", "--name finance", "--output json"],
async run(config: Config, flags: GlobalFlags) {
const serverName = (flags.name as string) || "";
const type = (flags.type as string) || "OFFICIAL";
@@ -85,7 +84,7 @@ export default defineCommand({
const msg = (dataField.errorMsg as string | undefined) ?? code;
const hint =
code === "BailianGateway.Login.NotLogined"
? "Run `bl auth login --console` to refresh your console session."
? `Run \`${config.binName} auth login --console\` to refresh your console session.`
: undefined;
throw new BailianError(`Console gateway: ${msg}`, ExitCode.AUTH, hint);
}
@@ -6,33 +6,32 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult } from "../../output/output.ts";
import { ensureApiKey } from "../../utils/ensure-key.ts";
import { failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult } from "bailian-cli-runtime";
import { ensureApiKey } from "bailian-cli-runtime";
export default defineCommand({
name: "mcp tools",
description: "List tools exposed by an MCP server (tools/list)",
skipDefaultApiKeySetup: true,
usage: "bl mcp tools <server-code> [--url <url>]",
usageArgs: "<server-code> [--url <url>]",
options: [
{
flag: "<server-code>",
description: "Server code from `bl mcp list` (e.g. market-cmapi00073529)",
description: "Server code from `mcp list` (e.g. market-cmapi00073529)",
required: true,
},
{ flag: "--url <url>", description: "Override the MCP endpoint URL (for non-Bailian servers)" },
],
examples: [
"bl mcp tools market-cmapi00073529",
"bl mcp tools market-cmapi00073529 --output json",
"bl mcp tools my-server --url https://example.com/mcp",
exampleArgs: [
"market-cmapi00073529",
"market-cmapi00073529 --output json",
"my-server --url https://example.com/mcp",
],
async run(config: Config, flags: GlobalFlags) {
const positional =
((flags as Record<string, unknown>)._positional as string[] | undefined) ?? [];
const code = positional[0];
if (!code) failIfMissing("server-code", "bl mcp tools <server-code>");
if (!code) failIfMissing("server-code", cmdUsage(config, "<server-code>"));
const url = (flags.url as string) || bailianMcpUrl(config.baseUrl, code!);
const format = detectOutputFormat(config.output);
@@ -8,13 +8,12 @@ import {
type MemoryAddRequest,
type MemoryAddResponse,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "memory add",
description: "Add memory from messages or custom content",
usage: "bl memory add --user-id <id> [--messages <json>] [--content <text>] [flags]",
usageArgs: "--user-id <id> [--messages <json>] [--content <text>] [flags]",
options: [
{ flag: "--user-id <id>", description: "User ID (required)", required: true },
{
@@ -25,14 +24,14 @@ export default defineCommand({
{ flag: "--profile-schema <id>", description: "Profile schema ID for user profiling" },
{ flag: "--memory-library-id <id>", description: "Memory library ID (isolate memory space)" },
],
examples: [
'bl memory add --user-id user1 --content "The user likes Python programming"',
'bl memory add --user-id user1 --messages \'[{"role":"user","content":"I like traveling"}]\'',
'bl memory add --user-id user1 --content "Lives in Beijing" --profile-schema schema_xxx',
exampleArgs: [
'--user-id user1 --content "The user likes Python programming"',
'--user-id user1 --messages \'[{"role":"user","content":"I like traveling"}]\'',
'--user-id user1 --content "Lives in Beijing" --profile-schema schema_xxx',
],
async run(config: Config, flags: GlobalFlags) {
const userId = flags.userId as string;
if (!userId) failIfMissing("user-id", "bl memory add --user-id <id>");
if (!userId) failIfMissing("user-id", cmdUsage(config, "--user-id <id>"));
const body: MemoryAddRequest = { user_id: userId };
@@ -6,25 +6,24 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "memory delete",
description: "Delete a memory node",
usage: "bl memory delete --node-id <id> --user-id <id>",
usageArgs: "--node-id <id> --user-id <id>",
options: [
{ flag: "--node-id <id>", description: "Memory node ID (required)", required: true },
{ flag: "--user-id <id>", description: "User ID (required)", required: true },
{ flag: "--memory-library-id <id>", description: "Memory library ID (non-default library)" },
],
examples: ["bl memory delete --node-id node_xxx --user-id user1"],
exampleArgs: ["--node-id node_xxx --user-id user1"],
async run(config: Config, flags: GlobalFlags) {
const nodeId = flags.nodeId as string;
if (!nodeId) failIfMissing("node-id", "bl memory delete --node-id <id> --user-id <id>");
if (!nodeId) failIfMissing("node-id", cmdUsage(config, "--node-id <id> --user-id <id>"));
const userId = flags.userId as string;
if (!userId) failIfMissing("user-id", "bl memory delete --node-id <id> --user-id <id>");
if (!userId) failIfMissing("user-id", cmdUsage(config, "--node-id <id> --user-id <id>"));
const format = detectOutputFormat(config.output);
const params = new URLSearchParams({ user_id: userId });
@@ -7,26 +7,22 @@ import {
type GlobalFlags,
type MemoryNodeListResponse,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "memory list",
description: "List memory nodes for a user",
usage: "bl memory list --user-id <id> [flags]",
usageArgs: "--user-id <id> [flags]",
options: [
{ flag: "--user-id <id>", description: "User ID (required)", required: true },
{ flag: "--page-size <n>", description: "Results per page (default: 10)", type: "number" },
{ flag: "--page <n>", description: "Page number (default: 1)", type: "number" },
{ flag: "--memory-library-id <id>", description: "Memory library ID" },
],
examples: [
"bl memory list --user-id user1",
"bl memory list --user-id user1 --page-size 20 --page 2",
],
exampleArgs: ["--user-id user1", "--user-id user1 --page-size 20 --page 2"],
async run(config: Config, flags: GlobalFlags) {
const userId = flags.userId as string;
if (!userId) failIfMissing("user-id", "bl memory list --user-id <id>");
if (!userId) failIfMissing("user-id", cmdUsage(config, "--user-id <id>"));
const format = detectOutputFormat(config.output);
const params = new URLSearchParams();
@@ -8,13 +8,12 @@ import {
type ProfileSchemaCreateRequest,
type ProfileSchemaCreateResponse,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "memory profile create",
description: "Create a user profile schema for memory profiling",
usage: "bl memory profile create --name <name> --attributes <json> [flags]",
usageArgs: "--name <name> --attributes <json> [flags]",
options: [
{ flag: "--name <name>", description: "Schema name (required)", required: true },
{ flag: "--description <text>", description: "Schema description" },
@@ -24,16 +23,16 @@ export default defineCommand({
required: true,
},
],
examples: [
'bl memory profile create --name "user_basic" --attributes \'[{"name":"age","description":"age"},{"name":"hobby","description":"hobby"}]\'',
exampleArgs: [
'--name "user_basic" --attributes \'[{"name":"age","description":"age"},{"name":"hobby","description":"hobby"}]\'',
],
async run(config: Config, flags: GlobalFlags) {
const name = flags.name as string;
if (!name) failIfMissing("name", "bl memory profile create --name <name> --attributes <json>");
if (!name) failIfMissing("name", cmdUsage(config, "--name <name> --attributes <json>"));
const attrStr = flags.attributes as string;
if (!attrStr)
failIfMissing("attributes", "bl memory profile create --name <name> --attributes <json>");
failIfMissing("attributes", cmdUsage(config, "--name <name> --attributes <json>"));
let attributes;
try {
@@ -7,25 +7,23 @@ import {
type GlobalFlags,
type UserProfileResponse,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "memory profile get",
description: "Get user profile by schema ID and user ID",
usage: "bl memory profile get --schema-id <id> --user-id <id>",
usageArgs: "--schema-id <id> --user-id <id>",
options: [
{ flag: "--schema-id <id>", description: "Profile schema ID (required)", required: true },
{ flag: "--user-id <id>", description: "User ID (required)", required: true },
],
examples: ["bl memory profile get --schema-id schema_xxx --user-id user1"],
exampleArgs: ["--schema-id schema_xxx --user-id user1"],
async run(config: Config, flags: GlobalFlags) {
const schemaId = flags.schemaId as string;
if (!schemaId)
failIfMissing("schema-id", "bl memory profile get --schema-id <id> --user-id <id>");
if (!schemaId) failIfMissing("schema-id", cmdUsage(config, "--schema-id <id> --user-id <id>"));
const userId = flags.userId as string;
if (!userId) failIfMissing("user-id", "bl memory profile get --schema-id <id> --user-id <id>");
if (!userId) failIfMissing("user-id", cmdUsage(config, "--schema-id <id> --user-id <id>"));
const format = detectOutputFormat(config.output);
const params = new URLSearchParams({ user_id: userId });
@@ -8,13 +8,12 @@ import {
type MemorySearchRequest,
type MemorySearchResponse,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "memory search",
description: "Search memory nodes by query or messages",
usage: "bl memory search --user-id <id> [--query <text>] [flags]",
usageArgs: "--user-id <id> [--query <text>] [flags]",
options: [
{ flag: "--user-id <id>", description: "User ID (required)", required: true },
{ flag: "--query <text>", description: "Search query text" },
@@ -26,13 +25,13 @@ export default defineCommand({
},
{ flag: "--memory-library-id <id>", description: "Memory library ID" },
],
examples: [
'bl memory search --user-id user1 --query "programming preferences"',
'bl memory search --user-id user1 --messages \'[{"role":"user","content":"recommend a book"}]\' --top-k 5',
exampleArgs: [
'--user-id user1 --query "programming preferences"',
'--user-id user1 --messages \'[{"role":"user","content":"recommend a book"}]\' --top-k 5',
],
async run(config: Config, flags: GlobalFlags) {
const userId = flags.userId as string;
if (!userId) failIfMissing("user-id", "bl memory search --user-id <id>");
if (!userId) failIfMissing("user-id", cmdUsage(config, "--user-id <id>"));
const body: MemorySearchRequest = { user_id: userId };
@@ -7,13 +7,12 @@ import {
type GlobalFlags,
type MemoryNodeUpdateRequest,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "memory update",
description: "Update a memory node content",
usage: "bl memory update --node-id <id> --user-id <id> --content <text>",
usageArgs: "--node-id <id> --user-id <id> --content <text>",
options: [
{ flag: "--node-id <id>", description: "Memory node ID (required)", required: true },
{ flag: "--user-id <id>", description: "User ID (required)", required: true },
@@ -24,21 +23,19 @@ export default defineCommand({
},
{ flag: "--memory-library-id <id>", description: "Memory library ID (non-default library)" },
],
examples: [
'bl memory update --node-id node_xxx --user-id user1 --content "updated memory content"',
],
exampleArgs: ['--node-id node_xxx --user-id user1 --content "updated memory content"'],
async run(config: Config, flags: GlobalFlags) {
const nodeId = flags.nodeId as string;
if (!nodeId)
failIfMissing("node-id", "bl memory update --node-id <id> --user-id <id> --content <text>");
failIfMissing("node-id", cmdUsage(config, "--node-id <id> --user-id <id> --content <text>"));
const userId = flags.userId as string;
if (!userId)
failIfMissing("user-id", "bl memory update --node-id <id> --user-id <id> --content <text>");
failIfMissing("user-id", cmdUsage(config, "--node-id <id> --user-id <id> --content <text>"));
const content = flags.content as string;
if (!content)
failIfMissing("content", "bl memory update --node-id <id> --user-id <id> --content <text>");
failIfMissing("content", cmdUsage(config, "--node-id <id> --user-id <id> --content <text>"));
const body: MemoryNodeUpdateRequest = {
user_id: userId,
@@ -19,8 +19,8 @@ import {
resolveOutputDir,
resolveCredential,
} from "bailian-cli-core";
import { promptText, failIfMissing } from "../../output/prompt.ts";
import { emitResult } from "../../output/output.ts";
import { promptText, failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult } from "bailian-cli-runtime";
interface VoiceEntry {
voice: string;
@@ -121,9 +121,8 @@ function buildWavHeader(dataLength: number): Buffer {
}
export default defineCommand({
name: "omni",
description: "Multimodal chat with text + audio output (Qwen-Omni)",
usage: "bl omni --message <text> [flags]",
usageArgs: "--message <text> [flags]",
options: [
{
flag: "--message <text>",
@@ -158,16 +157,16 @@ export default defineCommand({
{ flag: "--max-tokens <n>", description: "Maximum tokens to generate", type: "number" },
{ flag: "--temperature <n>", description: "Sampling temperature (0.0, 2.0]", type: "number" },
],
examples: [
"bl omni --list-voices",
'bl omni --message "Hello, who are you?"',
'bl omni --message "Describe this image" --image ./photo.jpg',
'bl omni --message "What is this audio saying?" --audio https://example.com/audio.wav',
'bl omni --message "Summarize this video" --video https://example.com/video.mp4',
'bl omni --message "What is this video about?" --video ./local-video.mp4 --text-only',
'bl omni --message "Answer in Sichuan dialect: How\'s the weather today?" --voice Sunny',
'bl omni --message "Hello" --text-only --output json',
'bl omni --message "Read this passage aloud" --audio-out greeting.wav',
exampleArgs: [
"--list-voices",
'--message "Hello, who are you?"',
'--message "Describe this image" --image ./photo.jpg',
'--message "What is this audio saying?" --audio https://example.com/audio.wav',
'--message "Summarize this video" --video https://example.com/video.mp4',
'--message "What is this video about?" --video ./local-video.mp4 --text-only',
'--message "Answer in Sichuan dialect: How\'s the weather today?" --voice Sunny',
'--message "Hello" --text-only --output json',
'--message "Read this passage aloud" --audio-out greeting.wav',
],
async run(config: Config, flags: GlobalFlags) {
if (flags.listVoices) {
@@ -190,7 +189,7 @@ export default defineCommand({
}
userMessages = [hint];
} else {
failIfMissing("message", "bl text omni --message <text>");
failIfMissing("message", cmdUsage(config, "--message <text>"));
}
}
@@ -1,6 +1,6 @@
import { readFile } from "node:fs/promises";
import { extname } from "node:path";
import type { PipelineDefinition } from "../../pipeline/types.ts";
import type { PipelineDefinition } from "bailian-cli-runtime";
export async function loadPipelineFile(filePath: string): Promise<PipelineDefinition> {
const raw = await readFile(filePath, "utf-8").catch((err: Error) => {
@@ -1,17 +1,16 @@
import { readFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";
import { defineCommand, type Config, type GlobalFlags } from "bailian-cli-core";
import { emitResult } from "../../output/output.ts";
import { initPipelineSteps } from "../../pipeline/init.ts";
import { executePipeline, streamPipelineEvents } from "../../pipeline/executor.ts";
import type { PipelineLifecycleEvent } from "../../pipeline/types.ts";
import { emitResult, cmdUsage } from "bailian-cli-runtime";
import { initPipelineSteps } from "bailian-cli-runtime";
import { executePipeline, streamPipelineEvents } from "bailian-cli-runtime";
import type { PipelineLifecycleEvent } from "bailian-cli-runtime";
import { loadPipelineFile } from "./load-file.ts";
export default defineCommand({
name: "pipeline run",
description: "Run a pipeline workflow definition",
skipDefaultApiKeySetup: true,
usage: "bl pipeline run <file> [flags]",
usageArgs: "<file> [flags]",
options: [
{ flag: "--input <json>", description: "Runtime input as inline JSON" },
{ flag: "--input-file <path>", description: "Runtime input from a JSON file" },
@@ -27,17 +26,19 @@ export default defineCommand({
type: "number",
},
],
examples: [
'bl pipeline run workflow.yaml --input \'{"brief":"hello"}\'',
"bl pipeline run workflow.json --input-file inputs.json --concurrency 3",
"bl pipeline run workflow.yaml --dry-run",
"bl pipeline run workflow.json --events jsonl",
"bl pipeline run workflow.yaml --output json",
exampleArgs: [
'workflow.yaml --input \'{"brief":"hello"}\'',
"workflow.json --input-file inputs.json --concurrency 3",
"workflow.yaml --dry-run",
"workflow.json --events jsonl",
"workflow.yaml --output json",
],
async run(config: Config, flags: GlobalFlags) {
const file = ((flags._positional as string[] | undefined) ?? [])[0] as string | undefined;
if (!file) {
process.stderr.write("Error: pipeline file is required\nUsage: bl pipeline run <file>\n");
process.stderr.write(
`Error: pipeline file is required\nUsage: ${cmdUsage(config, "<file>")}\n`,
);
process.exit(2);
}
@@ -1,25 +1,21 @@
import { resolve } from "node:path";
import { defineCommand, type Config, type GlobalFlags } from "bailian-cli-core";
import { emitResult } from "../../output/output.ts";
import { initPipelineSteps } from "../../pipeline/init.ts";
import { collectPipelineIssues, collectPipelineHints } from "../../pipeline/validation.ts";
import { emitResult, cmdUsage } from "bailian-cli-runtime";
import { initPipelineSteps } from "bailian-cli-runtime";
import { collectPipelineIssues, collectPipelineHints } from "bailian-cli-runtime";
import { loadPipelineFile } from "./load-file.ts";
export default defineCommand({
name: "pipeline validate",
description: "Validate a pipeline definition without executing",
skipDefaultApiKeySetup: true,
usage: "bl pipeline validate <file>",
usageArgs: "<file>",
options: [],
examples: [
"bl pipeline validate workflow.yaml",
"bl pipeline validate workflow.json --output json",
],
exampleArgs: ["workflow.yaml", "workflow.json --output json"],
async run(config: Config, flags: GlobalFlags) {
const file = ((flags._positional as string[] | undefined) ?? [])[0] as string | undefined;
if (!file) {
process.stderr.write(
"Error: pipeline file is required\nUsage: bl pipeline validate <file>\n",
`Error: pipeline file is required\nUsage: ${cmdUsage(config, "<file>")}\n`,
);
process.exit(2);
}
@@ -7,8 +7,8 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { emitResult } from "../../output/output.ts";
import { displayWidth, padEnd } from "../../output/cjk-width.ts";
import { emitResult } from "bailian-cli-runtime";
import { displayWidth, padEnd } from "bailian-cli-runtime";
const MODEL_LIST_API = "zeldaHttp.dashscopeModel./zelda/api/v1/modelCenter/listFoundationModels";
const MONITOR_API = "zeldaEasy.bailian-telemetry.monitor.getMonitorData";
@@ -235,10 +235,9 @@ function printTable(rows: CheckRow[], noColor: boolean): void {
}
export default defineCommand({
name: "quota check",
description: "Check current usage against rate limits",
skipDefaultApiKeySetup: true,
usage: "bl quota check [--model <model>] [flags]",
usageArgs: "[--model <model>] [flags]",
options: [
{
flag: "--model <model>",
@@ -259,12 +258,12 @@ export default defineCommand({
type: "number",
},
],
examples: [
"bl quota check",
"bl quota check --model qwen3.6-plus",
"bl quota check --period 5",
"bl quota check --model qwen3.6-plus,qwen-turbo",
"bl quota check --output json",
exampleArgs: [
"",
"--model qwen3.6-plus",
"--period 5",
"--model qwen3.6-plus,qwen-turbo",
"--output json",
],
async run(config: Config, flags: GlobalFlags) {
const modelFlag = (flags.model as string) || undefined;
@@ -7,8 +7,8 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { emitResult } from "../../output/output.ts";
import { displayWidth, padEnd } from "../../output/cjk-width.ts";
import { emitResult } from "bailian-cli-runtime";
import { displayWidth, padEnd } from "bailian-cli-runtime";
const HISTORY_API = "zeldaEasy.broadscope-platform.modelInstance.listModelLimitApplications";
@@ -91,10 +91,9 @@ function printTable(records: LimitApplicationItem[], noColor: boolean, total: nu
}
export default defineCommand({
name: "quota history",
description: "View quota change history",
skipDefaultApiKeySetup: true,
usage: "bl quota history [flags]",
usageArgs: "[flags]",
options: [
{
flag: "--page <n>",
@@ -119,13 +118,7 @@ export default defineCommand({
type: "number",
},
],
examples: [
"bl quota history",
"bl quota history --page 2",
"bl quota history --page-size 20",
"bl quota history --model qwen-turbo",
"bl quota history --output json",
],
exampleArgs: ["", "--page 2", "--page-size 20", "--model qwen-turbo", "--output json"],
async run(config: Config, flags: GlobalFlags) {
const page = Number(flags.page) || 1;
const pageSize = Number(flags.pageSize) || 10;
@@ -152,7 +145,7 @@ export default defineCommand({
} catch (err) {
if (err instanceof BailianError && err.message.includes("NotLogined")) {
process.stderr.write(
"Error: session expired. Run `bl auth login --console` to re-authenticate.\n",
`Error: session expired. Run \`${config.binName} auth login --console\` to re-authenticate.\n`,
);
process.exit(1);
}
@@ -6,8 +6,8 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { emitResult } from "../../output/output.ts";
import { displayWidth, padEnd } from "../../output/cjk-width.ts";
import { emitResult } from "bailian-cli-runtime";
import { displayWidth, padEnd } from "bailian-cli-runtime";
const MODEL_LIST_API = "zeldaHttp.dashscopeModel./zelda/api/v1/modelCenter/listFoundationModels";
@@ -150,10 +150,9 @@ function printTable(models: ModelWithQpm[], noColor: boolean): void {
}
export default defineCommand({
name: "quota list",
description: "View model RPM/TPM rate limits",
skipDefaultApiKeySetup: true,
usage: "bl quota list [--model <model>] [flags]",
usageArgs: "[--model <model>] [flags]",
options: [
{
flag: "--model <model>",
@@ -174,12 +173,12 @@ export default defineCommand({
type: "number",
},
],
examples: [
"bl quota list",
"bl quota list --model qwen3.6-plus",
"bl quota list --model qwen3.6-plus,qwen-turbo",
"bl quota list --all",
"bl quota list --output json",
exampleArgs: [
"",
"--model qwen3.6-plus",
"--model qwen3.6-plus,qwen-turbo",
"--all",
"--output json",
],
async run(config: Config, flags: GlobalFlags) {
const modelFlag = (flags.model as string) || undefined;
@@ -7,7 +7,7 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { emitResult } from "../../output/output.ts";
import { emitResult } from "bailian-cli-runtime";
const MODEL_LIST_API = "zeldaHttp.dashscopeModel./zelda/api/v1/modelCenter/listFoundationModels";
const UPDATE_LIMITS_API = "zeldaEasy.broadscope-platform.modelInstance.updateFoundationModelLimits";
@@ -78,10 +78,9 @@ async function fetchModelQpmInfo(
}
export default defineCommand({
name: "quota request",
description: "Request a temporary quota increase",
skipDefaultApiKeySetup: true,
usage: "bl quota request --model <model> --tpm <value> [flags]",
usageArgs: "--model <model> --tpm <value> [flags]",
options: [
{
flag: "--model <model>",
@@ -108,10 +107,10 @@ export default defineCommand({
type: "number",
},
],
examples: [
"bl quota request --model qwen-turbo --tpm 100000",
"bl quota request --model qwen3.6-plus --tpm 8000000 --yes",
"bl quota request --model qwen-turbo --tpm 100000 --output json",
exampleArgs: [
"--model qwen-turbo --tpm 100000",
"--model qwen3.6-plus --tpm 8000000 --yes",
"--model qwen-turbo --tpm 100000 --output json",
],
async run(config: Config, flags: GlobalFlags) {
const modelName = flags.model as string;
@@ -147,7 +146,9 @@ export default defineCommand({
process.stderr.write(
`Error: model "${modelName}" not found or does not support self-service quota increase.\n`,
);
process.stderr.write("Hint: run `bl quota list` to view available models.\n");
process.stderr.write(
`Hint: run \`${config.binName} quota list\` to view available models.\n`,
);
process.exit(1);
}
@@ -186,7 +187,7 @@ export default defineCommand({
} catch (err) {
if (err instanceof BailianError && err.message.includes("NotLogined")) {
process.stderr.write(
"Error: session expired. Run `bl auth login --console` to re-authenticate.\n",
`Error: session expired. Run \`${config.binName} auth login --console\` to re-authenticate.\n`,
);
process.exit(1);
}
@@ -7,24 +7,23 @@ import {
isInteractive,
McpClient,
} from "bailian-cli-core";
import { createSpinner } from "../../output/progress.ts";
import { promptText, failIfMissing } from "../../output/prompt.ts";
import { emitResult } from "../../output/output.ts";
import { createSpinner } from "bailian-cli-runtime";
import { promptText, failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult } from "bailian-cli-runtime";
export default defineCommand({
name: "search web",
description: "Search the web using DashScope MCP WebSearch service",
usage: "bl search web --query <text> [flags]",
usageArgs: "--query <text> [flags]",
options: [
{ flag: "--query <text>", description: "Search query text", required: true },
{ flag: "--count <n>", description: "Number of search results (default: 10)", type: "number" },
{ flag: "--list-tools", description: "List available MCP tools and exit" },
],
examples: [
'bl search web --query "Alibaba Cloud Bailian latest features"',
'bl search web --query "TypeScript 5.9 new features" --count 5',
'bl search web --query "Today\'s news"',
"bl search web --list-tools",
exampleArgs: [
'--query "Alibaba Cloud Bailian latest features"',
'--query "TypeScript 5.9 new features" --count 5',
'--query "Today\'s news"',
"--list-tools",
],
async run(config: Config, flags: GlobalFlags) {
const mcpUrl = mcpWebSearchEndpoint(config.baseUrl);
@@ -56,7 +55,7 @@ export default defineCommand({
}
query = hint;
} else {
failIfMissing("query", "bl search web --query <text>");
failIfMissing("query", cmdUsage(config, "--query <text>"));
}
}
@@ -18,14 +18,13 @@ import {
type OutputFormat,
speechRecognizeEndpoint,
} from "bailian-cli-core";
import { poll } from "../../utils/polling.ts";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { poll } from "bailian-cli-runtime";
import { failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "speech recognize",
description: "Recognize speech from audio files (FunAudio-ASR)",
usage: "bl speech recognize --url <audio-url> [flags]",
usageArgs: "--url <audio-url> [flags]",
options: [
{
flag: "--url <url>",
@@ -51,14 +50,14 @@ export default defineCommand({
type: "number",
},
],
examples: [
"bl speech recognize --url https://example.com/audio.mp3",
"bl speech recognize --url https://example.com/a.mp3 --url https://example.com/b.mp3",
"bl speech recognize --url https://example.com/meeting.wav --diarization --speaker-count 3",
"bl speech recognize --url https://example.com/audio.mp3 --language zh",
"bl speech recognize --url https://example.com/audio.mp3 --vocabulary-id vocab-abc123",
"bl speech recognize --url https://example.com/audio.mp3 --out result.json",
"bl speech recognize --url https://example.com/audio.mp3 --no-wait --quiet",
exampleArgs: [
"--url https://example.com/audio.mp3",
"--url https://example.com/a.mp3 --url https://example.com/b.mp3",
"--url https://example.com/meeting.wav --diarization --speaker-count 3",
"--url https://example.com/audio.mp3 --language zh",
"--url https://example.com/audio.mp3 --vocabulary-id vocab-abc123",
"--url https://example.com/audio.mp3 --out result.json",
"--url https://example.com/audio.mp3 --no-wait --quiet",
],
async run(config: Config, flags: GlobalFlags) {
// Normalize --url to string[] (supports both single and repeated flags)
@@ -69,7 +68,7 @@ export default defineCommand({
rawUrls = [flags.url];
}
if (rawUrls.length === 0) {
failIfMissing("url", "bl speech recognize --url <audio-url>");
failIfMissing("url", cmdUsage(config, "--url <audio-url>"));
}
// Strict validation: --speaker-count requires --diarization
@@ -20,11 +20,11 @@ import {
DOCS_HOSTS,
} from "bailian-cli-core";
import { VOICE_TTS_PAGE } from "../../urls.ts";
import { downloadFile } from "../../utils/download.ts";
import { runConcurrent, downloadParallel, getConcurrency } from "../../utils/concurrent.ts";
import { promptText, promptSelect, failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { downloadFile } from "bailian-cli-runtime";
import { runConcurrent, downloadParallel, getConcurrency } from "bailian-cli-runtime";
import { promptText, promptSelect, failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { VOICE_TTS_PAGE } from "bailian-cli-runtime";
const COSYVOICE_CLONE_DESIGN_DOC = `${DOCS_HOSTS.cn}/cosyvoice-clone-design-api`;
@@ -147,9 +147,8 @@ function printVoiceList(model: string): void {
}
export default defineCommand({
name: "speech synthesize",
description: "Synthesize speech from text (CosyVoice TTS)",
usage: "bl speech synthesize --text <text> [flags]",
usageArgs: "--text <text> [flags]",
options: [
{ flag: "--text <text>", description: "Text to synthesize into speech", required: true },
{ flag: "--text-file <path>", description: "Read text from a file instead of --text" },
@@ -187,17 +186,17 @@ export default defineCommand({
},
{ flag: "--stream", description: "Stream raw PCM audio to stdout (pipe to player)" },
],
examples: [
"bl speech synthesize --list-voices --model cosyvoice-v3-flash",
'bl speech synthesize --text "Hello, I am Qwen" --voice <voice_id>',
'bl speech synthesize --text "Hello world" --voice <voice_id> --language en',
"bl speech synthesize --text-file script.txt --out speech.wav --voice <voice_id>",
'bl speech synthesize --text "Today is a good day" --voice <voice_id> --instruction "Use a gentle tone"',
'bl speech synthesize --text "Hello" --voice <voice_id> --format wav --sample-rate 24000',
exampleArgs: [
"--list-voices --model cosyvoice-v3-flash",
'--text "Hello, I am Qwen" --voice <voice_id>',
'--text "Hello world" --voice <voice_id> --language en',
"--text-file script.txt --out speech.wav --voice <voice_id>",
'--text "Today is a good day" --voice <voice_id> --instruction "Use a gentle tone"',
'--text "Hello" --voice <voice_id> --format wav --sample-rate 24000',
"# Stream to audio player (macOS)",
'bl speech synthesize --text "Hello" --voice <voice_id> --stream | afplay -',
'--text "Hello" --voice <voice_id> --stream | afplay -',
"# Pipe to ffplay",
'bl speech synthesize --text "Hello" --voice <voice_id> --stream | ffplay -nodisp -autoexit -f s16le -ar 24000 -ac 1 -',
'--text "Hello" --voice <voice_id> --stream | ffplay -nodisp -autoexit -f s16le -ar 24000 -ac 1 -',
],
async run(config: Config, flags: GlobalFlags) {
const model = (flags.model as string) || config.defaultSpeechModel || "cosyvoice-v3-flash";
@@ -229,7 +228,7 @@ export default defineCommand({
}
text = hint;
} else {
failIfMissing("text", "bl speech synthesize --text <text>");
failIfMissing("text", cmdUsage(config, "--text <text>"));
}
}
@@ -270,7 +269,7 @@ export default defineCommand({
const modelVoices = MODEL_VOICES[model];
if (modelVoices && modelVoices.length > 0) {
throw new BailianError(
`--voice is required.\nRun the following to see available voices:\n bl speech synthesize --list-voices --model ${model}\nBrowse more voices: ${VOICE_TTS_PAGE}`,
`--voice is required.\nRun the following to see available voices:\n ${cmdUsage(config, `--list-voices --model ${model}`)}\nBrowse more voices: ${VOICE_TTS_PAGE}`,
ExitCode.USAGE,
);
} else {
@@ -13,8 +13,8 @@ import {
type StreamChunk,
isInteractive,
} from "bailian-cli-core";
import { promptText, failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { promptText, failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { readFileSync } from "fs";
interface ParsedMessages {
@@ -68,9 +68,8 @@ function parseMessages(flags: GlobalFlags): ParsedMessages {
}
export default defineCommand({
name: "text chat",
description: "Send a chat completion (OpenAI compatible, DashScope)",
usage: "bl text chat --message <text> [flags]",
usageArgs: "--message <text> [flags]",
options: [
{ flag: "--model <model>", description: "Model ID (default: qwen3.7-max)" },
{
@@ -107,13 +106,13 @@ export default defineCommand({
type: "number",
},
],
examples: [
'bl text chat --message "What is Qwen?"',
'bl text chat --model qwen-max --system "You are a coding assistant." --message "Write fizzbuzz in Python"',
'bl text chat --message "Hello" --message "assistant:Hi!" --message "How are you?"',
"cat conversation.json | bl text chat --messages-file - --stream",
'bl text chat --message "Hello" --output json',
'bl text chat --model qwq-plus --message "Solve 1+1" --enable-thinking',
exampleArgs: [
'--message "What is Qwen?"',
'--model qwen-max --system "You are a coding assistant." --message "Write fizzbuzz in Python"',
'--message "Hello" --message "assistant:Hi!" --message "How are you?"',
"--messages-file - --stream",
'--message "Hello" --output json',
'--model qwq-plus --message "Solve 1+1" --enable-thinking',
],
async run(config: Config, flags: GlobalFlags) {
const { system, messages: parsedMessages } = parseMessages(flags);
@@ -130,7 +129,7 @@ export default defineCommand({
}
messages = [{ role: "user", content: hint }];
} else {
failIfMissing("message", "bl text chat --message <text>");
failIfMissing("message", cmdUsage(config, "--message <text>"));
}
}
@@ -6,8 +6,8 @@ import {
BailianError,
ExitCode,
} from "bailian-cli-core";
import { emitResult, emitBare } from "../../output/output.ts";
import { padEnd } from "../../output/cjk-width.ts";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { padEnd } from "bailian-cli-runtime";
import type { AddOrganizationMemberResponse } from "./types.ts";
import {
TOKEN_PLAN_AK_OPTIONS,
@@ -25,9 +25,8 @@ const API_PATH = "/tokenplan/organization/member-additions";
const DEFAULT_ORG_ROLE = "ORG_MEMBER";
export default defineCommand({
name: "token-plan add-member",
description: "Add a member to a Token Plan organization",
usage: "bl token-plan add-member --account-name <name> --org-id <id> [flags]",
usageArgs: "--account-name <name> --org-id <id> [flags]",
options: [
{ flag: "--account-name <name>", description: "Member display name", required: true },
{ flag: "--org-id <id>", description: "Organization ID", required: true },
@@ -42,10 +41,10 @@ export default defineCommand({
...TOKEN_PLAN_COMMON_QUERY_OPTIONS,
...TOKEN_PLAN_AK_OPTIONS,
],
examples: [
"bl token-plan add-member --account-name dev_user --org-id org_123",
"bl token-plan add-member --account-name admin_user --org-id org_123 --org-role-code ORG_ADMIN",
"bl token-plan add-member --account-name member1 --org-id org_123 --spec-type standard",
exampleArgs: [
"--account-name dev_user --org-id org_123",
"--account-name admin_user --org-id org_123 --org-role-code ORG_ADMIN",
"--account-name member1 --org-id org_123 --spec-type standard",
],
async run(config: Config, flags: GlobalFlags) {
const format = detectOutputFormat(config.output);
@@ -6,7 +6,7 @@ import {
BailianError,
ExitCode,
} from "bailian-cli-core";
import { emitResult, emitBare } from "../../output/output.ts";
import { emitResult, emitBare } from "bailian-cli-runtime";
import type { BatchAssignSeatsResponse } from "./types.ts";
import {
TOKEN_PLAN_AK_OPTIONS,
@@ -24,10 +24,8 @@ const API_ACTION = "BatchAssignSeats";
const API_PATH = "/tokenplan/subscription/seat-assignments";
export default defineCommand({
name: "token-plan assign-seats",
description: "Batch assign Token Plan seats to members",
usage:
"bl token-plan assign-seats --workspace-id <id> --seat-type <type> --account-id <id> [flags]",
usageArgs: "--workspace-id <id> --seat-type <type> --account-id <id> [flags]",
options: [
TOKEN_PLAN_WORKSPACE_OPTION,
{
@@ -47,9 +45,9 @@ export default defineCommand({
},
...TOKEN_PLAN_AK_OPTIONS,
],
examples: [
"bl token-plan assign-seats --workspace-id ws_456 --seat-type standard --account-id acc_123",
"bl token-plan assign-seats --workspace-id ws_456 --seat-type pro --account-id acc_1 --account-id acc_2",
exampleArgs: [
"--workspace-id ws_456 --seat-type standard --account-id acc_123",
"--workspace-id ws_456 --seat-type pro --account-id acc_1 --account-id acc_2",
],
async run(config: Config, flags: GlobalFlags) {
const format = detectOutputFormat(config.output);
@@ -6,8 +6,8 @@ import {
BailianError,
ExitCode,
} from "bailian-cli-core";
import { emitResult, emitBare } from "../../output/output.ts";
import { padEnd } from "../../output/cjk-width.ts";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { padEnd } from "bailian-cli-runtime";
import type { CreateTokenPlanKeyResponse } from "./types.ts";
import {
TOKEN_PLAN_AK_OPTIONS,
@@ -25,9 +25,8 @@ const API_ACTION = "CreateTokenPlanKey";
const API_PATH = "/tokenplan/api-keys";
export default defineCommand({
name: "token-plan create-key",
description: "Create a Token Plan API key for a seat",
usage: "bl token-plan create-key --account-id <id> --workspace-id <id> [flags]",
usageArgs: "--account-id <id> --workspace-id <id> [flags]",
options: [
{ flag: "--account-id <id>", description: "Target member account ID", required: true },
TOKEN_PLAN_WORKSPACE_OPTION,
@@ -35,9 +34,9 @@ export default defineCommand({
...TOKEN_PLAN_COMMON_QUERY_OPTIONS,
...TOKEN_PLAN_AK_OPTIONS,
],
examples: [
"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'",
exampleArgs: [
"--account-id acc_123 --workspace-id ws_456",
"--account-id acc_123 --workspace-id ws_456 --description 'Dev key'",
],
async run(config: Config, flags: GlobalFlags) {
const format = detectOutputFormat(config.output);
@@ -6,8 +6,8 @@ import {
BailianError,
ExitCode,
} from "bailian-cli-core";
import { emitResult, emitBare } from "../../output/output.ts";
import { padEnd } from "../../output/cjk-width.ts";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { padEnd } from "bailian-cli-runtime";
import type { GetSubscriptionSeatDetailsResponse, TokenPlanSeatDetail } from "./types.ts";
import {
TOKEN_PLAN_AK_OPTIONS,
@@ -23,9 +23,8 @@ const API_ACTION = "GetSubscriptionSeatDetails";
const API_PATH = "/tokenplan/subscription/seat-detail";
export default defineCommand({
name: "token-plan list-seats",
description: "List Token Plan subscription seat details",
usage: "bl token-plan list-seats [flags]",
usageArgs: "[flags]",
options: [
{ flag: "--page-no <n>", description: "Page number (default: 1)", type: "number" },
{ flag: "--page-size <n>", description: "Page size (default: 10)", type: "number" },
@@ -51,11 +50,7 @@ export default defineCommand({
},
...TOKEN_PLAN_AK_OPTIONS,
],
examples: [
"bl token-plan list-seats",
"bl token-plan list-seats --page-size 20 --status NORMAL",
"bl token-plan list-seats --query-assigned true --seat-type standard",
],
exampleArgs: ["", "--page-size 20 --status NORMAL", "--query-assigned true --seat-type standard"],
async run(config: Config, flags: GlobalFlags) {
const format = detectOutputFormat(config.output);
const credentials = resolveTokenPlanCredentials(config, flags);
@@ -2,15 +2,14 @@ import { execSync } from "child_process";
import { writeFileSync } from "fs";
import { join } from "path";
import { defineCommand, getConfigDir } from "bailian-cli-core";
import { CLI_VERSION } from "../version.ts";
import { NPM_PACKAGE, fetchLatestVersion } from "../utils/update-checker.ts";
import { fetchLatestVersion } from "bailian-cli-runtime";
const SKILL_SOURCE = "modelstudioai/cli";
const SKILL_INSTALL_CMD = `npx skills add ${SKILL_SOURCE} --all -g -y`;
/** Build the install command */
function detectInstallCommand(): { cmd: string; label: string } {
return { cmd: `npm install -g ${NPM_PACKAGE}@latest`, label: "npm" };
/** Build the install command for the given npm package. */
function detectInstallCommand(npmPackage: string): { cmd: string; label: string } {
return { cmd: `npm install -g ${npmPackage}@latest`, label: "npm" };
}
function updateAgentSkill(colors: { green: string; yellow: string; reset: string }): void {
@@ -29,25 +28,26 @@ function updateAgentSkill(colors: { green: string; yellow: string; reset: string
}
export default defineCommand({
name: "update",
description: "Update bl to the latest version",
description: "Update the CLI to the latest version",
skipDefaultApiKeySetup: true,
usage: "bl update",
examples: ["bl update"],
async run() {
exampleArgs: [""],
async run(config) {
const npmPackage = config.npmPackage!;
const binName = config.binName!;
const currentVersion = config.clientVersion!;
const isTTY = process.stderr.isTTY;
const green = isTTY ? "\x1b[32m" : "";
const yellow = isTTY ? "\x1b[33m" : "";
const reset = isTTY ? "\x1b[0m" : "";
process.stderr.write(`Current version: ${yellow}${CLI_VERSION}${reset}\n`);
process.stderr.write(`Current version: ${yellow}${currentVersion}${reset}\n`);
// Check latest version first
process.stderr.write("Checking for updates...\n");
const latest = await fetchLatestVersion(5000);
const latest = await fetchLatestVersion(5000, npmPackage);
if (latest && latest === CLI_VERSION) {
process.stderr.write(`${green}\u2713 Already up to date (${CLI_VERSION}).${reset}\n`);
if (latest && latest === currentVersion) {
process.stderr.write(`${green}\u2713 Already up to date (${currentVersion}).${reset}\n`);
updateAgentSkill({ green, yellow, reset });
return;
}
@@ -56,18 +56,18 @@ export default defineCommand({
process.stderr.write(`Latest version: ${green}${latest}${reset}\n\n`);
}
const { cmd, label } = detectInstallCommand();
process.stderr.write(`Updating ${NPM_PACKAGE} via ${label}...\n\n`);
const { cmd, label } = detectInstallCommand(npmPackage);
process.stderr.write(`Updating ${npmPackage} via ${label}...\n\n`);
try {
execSync(cmd, { stdio: "inherit" });
// Verify the installed version after update
try {
const rawVer = execSync("bl --version 2>/dev/null", { encoding: "utf-8" }).trim();
// bl --version outputs "bl X.Y.Z" — extract just the version number
const newVer = rawVer.replace(/^bl\s+/, "");
const rawVer = execSync(`${binName} --version 2>/dev/null`, { encoding: "utf-8" }).trim();
// `<bin> --version` outputs "<bin> X.Y.Z" — extract just the version number
const newVer = rawVer.replace(new RegExp(`^${binName}\\s+`), "");
process.stderr.write(
`\n${green}\u2713 Update complete: ${CLI_VERSION} \u2192 ${newVer}${reset}\n`,
`\n${green}\u2713 Update complete: ${currentVersion} \u2192 ${newVer}${reset}\n`,
);
// Update the cached state so the post-run notification doesn't fire
try {
@@ -7,8 +7,8 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { emitResult } from "../../output/output.ts";
import { displayWidth, padEnd } from "../../output/cjk-width.ts";
import { emitResult } from "bailian-cli-runtime";
import { displayWidth, padEnd } from "bailian-cli-runtime";
const FREE_TIER_API = "zeldaEasy.broadscope-bailian.freeTrial.queryFreeTierQuota";
const FREE_TIER_ONLY_STATUS_API = "zeldaEasy.broadscope-bailian.freeTrial.queryFreeTierOnlyStatus";
@@ -184,10 +184,9 @@ async function fetchAllModels(config: Config, token: string): Promise<ModelInfo[
}
export default defineCommand({
name: "usage free",
description: "Query free-tier quota for models (all models if --model is omitted)",
skipDefaultApiKeySetup: true,
usage: "bl usage free [--model <model>[,model2,...]] [flags]",
usageArgs: "[--model <model>[,model2,...]] [flags]",
options: [
{
flag: "--model <model>",
@@ -212,14 +211,14 @@ export default defineCommand({
type: "number",
},
],
examples: [
"bl usage free",
"bl usage free --model qwen3-max",
"bl usage free --model qwen3-max,qwen-turbo",
"bl usage free --expiring 30",
"bl usage free --sort remaining",
"bl usage free --model qwen-turbo --output json",
"bl usage free --model qwen3-max --console-region cn-beijing",
exampleArgs: [
"",
"--model qwen3-max",
"--model qwen3-max,qwen-turbo",
"--expiring 30",
"--sort remaining",
"--model qwen-turbo --output json",
"--model qwen3-max --console-region cn-beijing",
],
async run(config: Config, flags: GlobalFlags) {
const modelFlag = (flags.model as string) || undefined;
@@ -7,7 +7,7 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { emitResult } from "../../output/output.ts";
import { emitResult } from "bailian-cli-runtime";
const ACTIVATE_API = "zeldaEasy.broadscope-bailian.freeTrial.batchActivateFreeTierOnly";
const DEACTIVATE_API = "zeldaEasy.broadscope-bailian.freeTrial.batchDeactivateFreeTierOnly";
@@ -100,11 +100,10 @@ async function fetchAllModelNames(config: Config, token: string): Promise<string
}
export default defineCommand({
name: "usage freetier",
description:
"Enable or disable auto-stop for free-tier models. Enables by default; use --off to disable",
skipDefaultApiKeySetup: true,
usage: "bl usage freetier <--model <model>[,model2,...] | --all> [--off] [flags]",
usageArgs: "<--model <model>[,model2,...] | --all> [--off] [flags]",
options: [
{
flag: "--model <model>",
@@ -133,13 +132,13 @@ export default defineCommand({
type: "number",
},
],
examples: [
"bl usage freetier --model qwen3-max",
"bl usage freetier --model qwen3-max,qwen-turbo",
"bl usage freetier --all",
"bl usage freetier --on --model qwen3-max",
"bl usage freetier --off --model qwen3-max",
"bl usage freetier --off --all",
exampleArgs: [
"--model qwen3-max",
"--model qwen3-max,qwen-turbo",
"--all",
"--on --model qwen3-max",
"--off --model qwen3-max",
"--off --all",
],
async run(config: Config, flags: GlobalFlags) {
const modelFlag = (flags.model as string) || undefined;
@@ -6,8 +6,8 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { emitResult } from "../../output/output.ts";
import { displayWidth, padEnd } from "../../output/cjk-width.ts";
import { emitResult } from "bailian-cli-runtime";
import { displayWidth, padEnd } from "bailian-cli-runtime";
const OVERVIEW_API = "zeldaEasy.bailian-telemetry.model.getModelUsageStatistic";
const LIST_API = "zeldaEasy.bailian-telemetry.model.listModelUsageStatisticData";
@@ -101,9 +101,11 @@ function resolveWorkspaceId(config: Config, flagWorkspaceId?: string): string {
if (config.workspaceId) return config.workspaceId;
process.stderr.write(
"Error: workspace-id is required. Set via --workspace-id, BAILIAN_WORKSPACE_ID, or `bl config set workspace_id <id>`.\n",
`Error: workspace-id is required. Set via --workspace-id, BAILIAN_WORKSPACE_ID, or \`${config.binName} config set workspace_id <id>\`.\n`,
);
process.stderr.write(
`Hint: run \`${config.binName} workspace list\` to view available workspaces.\n`,
);
process.stderr.write("Hint: run `bl workspace list` to view available workspaces.\n");
process.exit(1);
}
@@ -283,10 +285,9 @@ function printModelTable(
}
export default defineCommand({
name: "usage stats",
description: "Query model usage statistics",
skipDefaultApiKeySetup: true,
usage: "bl usage stats [--model <model>] [--days <days>] [flags]",
usageArgs: "[--model <model>] [--days <days>] [flags]",
options: [
{
flag: "--model <model>",
@@ -315,14 +316,14 @@ export default defineCommand({
type: "number",
},
],
examples: [
"bl usage stats",
"bl usage stats --days 30",
"bl usage stats --model qwen-turbo",
"bl usage stats --model qwen-turbo --days 7",
"bl usage stats --model qwen3.6-plus,deepseek-v4-pro",
"bl usage stats --type Text --days 14",
"bl usage stats --output json",
exampleArgs: [
"",
"--days 30",
"--model qwen-turbo",
"--model qwen-turbo --days 7",
"--model qwen3.6-plus,deepseek-v4-pro",
"--type Text --days 14",
"--output json",
],
async run(config: Config, flags: GlobalFlags) {
const modelFlag = (flags.model as string) || undefined;
@@ -9,28 +9,27 @@ import {
BailianError,
ExitCode,
} from "bailian-cli-core";
import { downloadFile, formatBytes } from "../../utils/download.ts";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { downloadFile, formatBytes } from "bailian-cli-runtime";
import { failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "video download",
description: "Download a completed video by task ID",
usage: "bl video download --task-id <id> --out <path>",
usageArgs: "--task-id <id> --out <path>",
options: [
{ flag: "--task-id <id>", description: "Task ID to download from" },
{ flag: "--out <path>", description: "Output file path" },
],
examples: [
"bl video download --task-id 3b256896-xxxx --out video.mp4",
"bl video download --task-id 3b256896-xxxx --out video.mp4 --quiet",
exampleArgs: [
"--task-id 3b256896-xxxx --out video.mp4",
"--task-id 3b256896-xxxx --out video.mp4 --quiet",
],
async run(config: Config, flags: GlobalFlags) {
const taskId = flags.taskId as string | undefined;
if (!taskId) failIfMissing("task-id", "bl video download --task-id <id> --out <path>");
if (!taskId) failIfMissing("task-id", cmdUsage(config, "--task-id <id> --out <path>"));
const outPath = flags.out as string | undefined;
if (!outPath) failIfMissing("out", "bl video download --task-id <id> --out video.mp4");
if (!outPath) failIfMissing("out", cmdUsage(config, "--task-id <id> --out video.mp4"));
const format = detectOutputFormat(config.output);
@@ -18,20 +18,16 @@ import {
resolveBooleanFlag,
resolveWatermark,
} from "bailian-cli-core";
import { poll } from "../../utils/polling.ts";
import { downloadFile, formatBytes } from "../../utils/download.ts";
import { promptText, failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import {
BOOL_FLAG_PROMPT_EXTEND_API_DEFAULT,
BOOL_FLAG_WATERMARK,
} from "../../utils/flag-descriptions.ts";
import { poll } from "bailian-cli-runtime";
import { downloadFile, formatBytes } from "bailian-cli-runtime";
import { promptText, failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { BOOL_FLAG_PROMPT_EXTEND_API_DEFAULT, BOOL_FLAG_WATERMARK } from "bailian-cli-runtime";
export default defineCommand({
name: "video edit",
description:
"Edit a video with happyhorse-1.0-video-edit (style transfer, object replacement, etc.)",
usage: "bl video edit --video <url> --prompt <text> [flags]",
usageArgs: "--video <url> --prompt <text> [flags]",
options: [
{ flag: "--model <model>", description: "Model ID (default: happyhorse-1.0-video-edit)" },
{
@@ -80,11 +76,11 @@ export default defineCommand({
type: "number",
},
],
examples: [
'bl video edit --video https://example.com/input.mp4 --prompt "Convert the entire scene to claymation style"',
'bl video edit --video https://example.com/input.mp4 --prompt "Replace the outfit with the style shown in the image" --ref-image https://example.com/clothes.png',
'bl video edit --video https://example.com/input.mp4 --prompt "Convert to anime style" --resolution 720P --download output.mp4',
'bl video edit --video https://example.com/input.mp4 --prompt "Put clothes on the kitten in the video" --watermark false',
exampleArgs: [
'--video https://example.com/input.mp4 --prompt "Convert the entire scene to claymation style"',
'--video https://example.com/input.mp4 --prompt "Replace the outfit with the style shown in the image" --ref-image https://example.com/clothes.png',
'--video https://example.com/input.mp4 --prompt "Convert to anime style" --resolution 720P --download output.mp4',
'--video https://example.com/input.mp4 --prompt "Put clothes on the kitten in the video" --watermark false',
],
async run(config: Config, flags: GlobalFlags) {
// --- Validate video URL ---
@@ -98,7 +94,7 @@ export default defineCommand({
}
videoUrl = hint;
} else {
failIfMissing("video", "bl video edit --video <url> --prompt <text>");
failIfMissing("video", cmdUsage(config, "--video <url> --prompt <text>"));
}
}
@@ -18,21 +18,17 @@ import {
resolveBooleanFlag,
resolveWatermark,
} from "bailian-cli-core";
import { poll } from "../../utils/polling.ts";
import { downloadFile, formatBytes } from "../../utils/download.ts";
import { runConcurrent, getConcurrency } from "../../utils/concurrent.ts";
import { promptText, failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import {
BOOL_FLAG_PROMPT_EXTEND_API_DEFAULT,
BOOL_FLAG_WATERMARK,
} from "../../utils/flag-descriptions.ts";
import { poll } from "bailian-cli-runtime";
import { downloadFile, formatBytes } from "bailian-cli-runtime";
import { runConcurrent, getConcurrency } from "bailian-cli-runtime";
import { promptText, failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { BOOL_FLAG_PROMPT_EXTEND_API_DEFAULT, BOOL_FLAG_WATERMARK } from "bailian-cli-runtime";
export default defineCommand({
name: "video generate",
description:
"Generate a video from text or image (happyhorse-1.1-t2v / happyhorse-1.1-i2v / wan2.6-t2v)",
usage: "bl video generate --prompt <text> [--image <url>] [flags]",
usageArgs: "--prompt <text> [--image <url>] [flags]",
options: [
{
flag: "--model <model>",
@@ -72,12 +68,12 @@ export default defineCommand({
type: "number",
},
],
examples: [
'bl video generate --prompt "A person reading a book, static shot"',
'bl video generate --prompt "Ocean waves at sunset." --download sunset.mp4',
'bl video generate --image https://example.com/cat.png --prompt "Make the cat in the scene move"',
'bl video generate --prompt "Mountain landscape" --resolution 720P --duration 5',
'bl video generate --prompt "A cat playing with a ball" --watermark false',
exampleArgs: [
'--prompt "A person reading a book, static shot"',
'--prompt "Ocean waves at sunset." --download sunset.mp4',
'--image https://example.com/cat.png --prompt "Make the cat in the scene move"',
'--prompt "Mountain landscape" --resolution 720P --duration 5',
'--prompt "A cat playing with a ball" --watermark false',
],
async run(config: Config, flags: GlobalFlags) {
let prompt = flags.prompt as string | undefined;
@@ -91,7 +87,7 @@ export default defineCommand({
}
prompt = hint;
} else {
failIfMissing("prompt", "bl video generate --prompt <text>");
failIfMissing("prompt", cmdUsage(config, "--prompt <text>"));
}
}
@@ -18,20 +18,16 @@ import {
resolveBooleanFlag,
resolveWatermark,
} from "bailian-cli-core";
import { poll } from "../../utils/polling.ts";
import { downloadFile, formatBytes } from "../../utils/download.ts";
import { promptText, failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import {
BOOL_FLAG_PROMPT_EXTEND_API_DEFAULT,
BOOL_FLAG_WATERMARK,
} from "../../utils/flag-descriptions.ts";
import { poll } from "bailian-cli-runtime";
import { downloadFile, formatBytes } from "bailian-cli-runtime";
import { promptText, failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { BOOL_FLAG_PROMPT_EXTEND_API_DEFAULT, BOOL_FLAG_WATERMARK } from "bailian-cli-runtime";
export default defineCommand({
name: "video ref",
description:
"Reference-to-video generation (happyhorse-1.1-r2v / wan2.6-r2v): multi-subject, multi-shot with voice",
usage: "bl video ref --prompt <text> --image <url>... [--ref-video <url>...] [flags]",
usageArgs: "--prompt <text> --image <url>... [--ref-video <url>...] [flags]",
options: [
{ flag: "--model <model>", description: "Model ID (default: happyhorse-1.1-r2v)" },
{
@@ -87,12 +83,12 @@ export default defineCommand({
type: "number",
},
],
examples: [
'bl video ref --prompt "Image1 running on the grass" --image person.jpg',
'bl video ref --prompt "Video 1 plays guitar, Image 1 walks over" --ref-video scene.mp4 --image person.jpg',
'bl video ref --prompt "Image 1 speaks" --image person.jpg --image-voice voice.mp3 --resolution 1080P',
'bl video ref --prompt "Image 1 and Image 2 have a conversation" --image a.jpg --image b.jpg --image-voice va.mp3 --image-voice vb.mp3',
'bl video ref --prompt "Image 1 drinks water" --image person.jpg --watermark false',
exampleArgs: [
'--prompt "Image1 running on the grass" --image person.jpg',
'--prompt "Video 1 plays guitar, Image 1 walks over" --ref-video scene.mp4 --image person.jpg',
'--prompt "Image 1 speaks" --image person.jpg --image-voice voice.mp3 --resolution 1080P',
'--prompt "Image 1 and Image 2 have a conversation" --image a.jpg --image b.jpg --image-voice va.mp3 --image-voice vb.mp3',
'--prompt "Image 1 drinks water" --image person.jpg --watermark false',
],
async run(config: Config, flags: GlobalFlags) {
// --- Validate prompt ---
@@ -108,7 +104,7 @@ export default defineCommand({
}
prompt = hint;
} else {
failIfMissing("prompt", "bl video ref --prompt <text> --image <url>");
failIfMissing("prompt", cmdUsage(config, "--prompt <text> --image <url>"));
}
}
@@ -119,7 +115,7 @@ export default defineCommand({
throw new BailianError(
"At least one --image or --ref-video is required.",
ExitCode.USAGE,
'bl video ref --prompt "description" --image person.jpg',
cmdUsage(config, '--prompt "description" --image person.jpg'),
);
}
@@ -7,21 +7,20 @@ import {
type GlobalFlags,
type DashScopeTaskResponse,
} from "bailian-cli-core";
import { failIfMissing } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { failIfMissing, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
export default defineCommand({
name: "video task get",
description: "Query async task status",
usage: "bl video task get --task-id <id>",
usageArgs: "--task-id <id>",
options: [{ flag: "--task-id <id>", description: "Async task ID" }],
examples: [
"bl video task get --task-id 3b256896-3e70-xxxx-xxxx-xxxxxxxxxxxx",
"bl video task get --task-id 3b256896-3e70-xxxx --output json",
exampleArgs: [
"--task-id 3b256896-3e70-xxxx-xxxx-xxxxxxxxxxxx",
"--task-id 3b256896-3e70-xxxx --output json",
],
async run(config: Config, flags: GlobalFlags) {
const taskId = flags.taskId as string | undefined;
if (!taskId) failIfMissing("task-id", "bl video task get --task-id <id>");
if (!taskId) failIfMissing("task-id", cmdUsage(config, "--task-id <id>"));
const format = detectOutputFormat(config.output);
@@ -15,8 +15,8 @@ import {
ExitCode,
isLocalFile,
} from "bailian-cli-core";
import { promptText } from "../../output/prompt.ts";
import { emitResult, emitBare } from "../../output/output.ts";
import { promptText, cmdUsage } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { readFileSync, existsSync } from "fs";
import { extname } from "path";
@@ -57,9 +57,8 @@ async function toImageUrl(image: string): Promise<string> {
}
export default defineCommand({
name: "vision describe",
description: "Describe an image or video using Qwen-VL",
usage: "bl vision describe --image <path-or-url> [--video <url>] [--prompt <text>]",
usageArgs: "--image <path-or-url> [--video <url>] [--prompt <text>]",
options: [
{ flag: "--image <path-or-url>", description: "Local image path or URL" },
{
@@ -70,12 +69,12 @@ export default defineCommand({
{ flag: "--prompt <text>", description: "Question about the content (default: auto-detected)" },
{ flag: "--model <model>", description: "Vision model (default: qwen3-vl-plus)" },
],
examples: [
"bl vision describe --image photo.jpg",
'bl vision describe --image https://example.com/photo.jpg --prompt "What breed is this dog?"',
'bl vision describe --video https://example.com/video.mp4 --prompt "Summarize the video content"',
"bl vision describe --video ./local-video.mp4",
'bl vision describe --image photo.png --prompt "Extract the text" --model qwen-vl-plus',
exampleArgs: [
"--image photo.jpg",
'--image https://example.com/photo.jpg --prompt "What breed is this dog?"',
'--video https://example.com/video.mp4 --prompt "Summarize the video content"',
"--video ./local-video.mp4",
'--image photo.png --prompt "Extract the text" --model qwen-vl-plus',
],
async run(config: Config, flags: GlobalFlags) {
let image = (flags.image ?? (flags._positional as string[] | undefined)?.[0]) as
@@ -113,7 +112,7 @@ export default defineCommand({
throw new BailianError(
"Missing required argument --image or --video.",
ExitCode.USAGE,
"bl vision describe --image <path-or-url>\nbl vision describe --video <url-or-path>",
`${cmdUsage(config, "--image <path-or-url>")}\n${cmdUsage(config, "--video <url-or-path>")}`,
);
}
}
@@ -6,8 +6,8 @@ import {
type Config,
type GlobalFlags,
} from "bailian-cli-core";
import { emitResult } from "../../output/output.ts";
import { displayWidth, padEnd } from "../../output/cjk-width.ts";
import { emitResult } from "bailian-cli-runtime";
import { displayWidth, padEnd } from "bailian-cli-runtime";
const LIST_WORKSPACES_API = "zeldaEasy.bailian-dash-workspace.space.listWorkspaces";
@@ -76,10 +76,9 @@ function printTable(workspaces: WorkspaceInfo[], noColor: boolean): void {
}
export default defineCommand({
name: "workspace list",
description: "List all workspaces",
skipDefaultApiKeySetup: true,
usage: "bl workspace list [flags]",
usageArgs: "[flags]",
options: [
{
flag: "--list <n>",
@@ -96,7 +95,7 @@ export default defineCommand({
type: "number",
},
],
examples: ["bl workspace list", "bl workspace list --list 5", "bl workspace list --output json"],
exampleArgs: ["", "--list 5", "--output json"],
async run(config: Config, flags: GlobalFlags) {
const limit = Number(flags.list) || 0;
const format = detectOutputFormat(config.output);
+76
View File
@@ -0,0 +1,76 @@
// Command library for bailian-cli products. Exposes individual command
// implementations only — no path presets or capability groups. Each product
// entrypoint (bl / rag / …) builds its own `{ "<path>": command }` map, so the
// command paths a product exposes are a product decision, not baked in here.
export { default as authLogin } from "./commands/auth/login.ts";
export { default as authStatus } from "./commands/auth/status.ts";
export { default as authLogout } from "./commands/auth/logout.ts";
export { default as textChat } from "./commands/text/chat.ts";
export { default as textOmni } from "./commands/omni/chat.ts";
export { default as imageGenerate } from "./commands/image/generate.ts";
export { default as imageEdit } from "./commands/image/edit.ts";
export { default as videoGenerate } from "./commands/video/generate.ts";
export { default as videoEdit } from "./commands/video/edit.ts";
export { default as videoRef } from "./commands/video/ref.ts";
export { default as videoTaskGet } from "./commands/video/task-get.ts";
export { default as videoDownload } from "./commands/video/download.ts";
export { default as visionDescribe } from "./commands/vision/describe.ts";
export { default as configShow } from "./commands/config/show.ts";
export { default as configSet } from "./commands/config/set.ts";
export { default as update } from "./commands/update.ts";
export { default as appCall } from "./commands/app/call.ts";
export { default as appList } from "./commands/app/list.ts";
export { default as memoryAdd } from "./commands/memory/add.ts";
export { default as memorySearch } from "./commands/memory/search.ts";
export { default as memoryList } from "./commands/memory/list.ts";
export { default as memoryUpdate } from "./commands/memory/update.ts";
export { default as memoryDelete } from "./commands/memory/delete.ts";
export { default as memoryProfileCreate } from "./commands/memory/profile-create.ts";
export { default as memoryProfileGet } from "./commands/memory/profile-get.ts";
export { default as knowledgeRetrieve } from "./commands/knowledge/retrieve.ts";
export { default as mcpCall } from "./commands/mcp/call.ts";
export { default as mcpList } from "./commands/mcp/list.ts";
export { default as mcpTools } from "./commands/mcp/tools.ts";
export { default as searchWeb } from "./commands/search/web.ts";
export { default as speechSynthesize } from "./commands/speech/synthesize.ts";
export { default as speechRecognize } from "./commands/speech/recognize.ts";
export { default as fileUpload } from "./commands/file/upload.ts";
export { default as consoleCall } from "./commands/console/call.ts";
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 pipelineRun } from "./commands/pipeline/run.ts";
export { default as pipelineValidate } from "./commands/pipeline/validate.ts";
export { default as advisorRecommend } from "./commands/advisor/recommend.ts";
export { default as workspaceList } from "./commands/workspace/list.ts";
export { default as quotaList } from "./commands/quota/list.ts";
export { default as quotaRequest } from "./commands/quota/request.ts";
export { default as quotaHistory } from "./commands/quota/history.ts";
export { default as quotaCheck } from "./commands/quota/check.ts";
export { default as datasetUpload } from "./commands/dataset/upload.ts";
export { default as datasetList } from "./commands/dataset/list.ts";
export { default as datasetGet } from "./commands/dataset/get.ts";
export { default as datasetDelete } from "./commands/dataset/delete.ts";
export { default as datasetValidate } from "./commands/dataset/validate.ts";
export { default as finetuneCreate } from "./commands/finetune/create.ts";
export { default as finetuneList } from "./commands/finetune/list.ts";
export { default as finetuneGet } from "./commands/finetune/get.ts";
export { default as finetuneCancel } from "./commands/finetune/cancel.ts";
export { default as finetuneDelete } from "./commands/finetune/delete.ts";
export { default as finetuneLogs } from "./commands/finetune/logs.ts";
export { default as finetuneCheckpoints } from "./commands/finetune/checkpoints.ts";
export { default as finetuneExport } from "./commands/finetune/export.ts";
export { default as finetuneWatch } from "./commands/finetune/watch.ts";
export { default as finetuneCapability } from "./commands/finetune/capability.ts";
export { default as deployCreate } from "./commands/deploy/create.ts";
export { default as deployList } from "./commands/deploy/list.ts";
export { default as deployGet } from "./commands/deploy/get.ts";
export { default as deployModels } from "./commands/deploy/models.ts";
export { default as deployScale } from "./commands/deploy/scale.ts";
export { default as deployUpdate } from "./commands/deploy/update.ts";
export { default as deployDelete } from "./commands/deploy/delete.ts";
export { default as tokenPlanListSeats } from "./commands/token-plan/list-seats.ts";
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";
+21
View File
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "esnext",
"lib": ["es2023"],
"moduleDetection": "force",
"module": "nodenext",
"moduleResolution": "nodenext",
"customConditions": ["@bailian-cli/source"],
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
"noUnusedLocals": true,
"declaration": true,
"noEmit": true,
"allowImportingTsExtensions": true,
"esModuleInterop": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true
}
}
+20
View File
@@ -0,0 +1,20 @@
import { defineConfig } from "vite-plus";
export default defineConfig({
pack: {
minify: true,
dts: {
tsgo: true,
},
exports: {
devExports: "@bailian-cli/source",
},
},
lint: {
options: {
typeAware: true,
typeCheck: true,
},
},
fmt: {},
});
+9 -2
View File
@@ -1,6 +1,6 @@
{
"name": "bailian-cli-core",
"version": "1.4.2",
"version": "1.5.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": {
@@ -19,11 +19,18 @@
"type": "module",
"types": "./dist/index.d.mts",
"exports": {
".": "./dist/index.mjs",
".": {
"@bailian-cli/source": "./src/index.ts",
"default": "./dist/index.mjs"
},
"./package.json": "./package.json"
},
"publishConfig": {
"access": "public",
"exports": {
".": "./dist/index.mjs",
"./package.json": "./package.json"
},
"registry": "https://registry.npmjs.org/"
},
"scripts": {
+4
View File
@@ -100,6 +100,10 @@ export function parseConfigFile(raw: unknown): ConfigFile {
export interface Config {
clientName?: string;
clientVersion?: string;
/** Product binary name (e.g. "bl", "rag"), injected by createCli for command-facing output. */
binName?: string;
/** npm package name for self-update (e.g. "bailian-cli", "bailian-cli-rag"), injected by createCli. */
npmPackage?: string;
apiKey?: string;
/** `DASHSCOPE_ACCESS_TOKEN` env (explicit override). */
accessTokenEnv?: string;
+3 -1
View File
@@ -142,7 +142,9 @@ export async function callConsoleGateway(
const innerData = json.data as Record<string, unknown> | undefined;
if (innerData?.success === false && innerData.errorCode) {
const errorCode = String(innerData.errorCode);
const rawErrorCode = innerData.errorCode;
const errorCode =
typeof rawErrorCode === "string" ? rawErrorCode : JSON.stringify(rawErrorCode);
const notLogined = errorCode.includes("NotLogined");
const errorMsg = typeof innerData.errorMsg === "string" ? innerData.errorMsg : undefined;
throw new BailianError(
+19 -9
View File
@@ -9,22 +9,33 @@ export interface OptionDef {
}
export interface Command {
name: string;
description: string;
usage?: string;
/**
* Argument portion of the usage line, WITHOUT the `<bin> <path>` prefix
* (e.g. "--index-id <id> --query <text> [flags]"). The runtime prepends the
* product binary name and the command's actual path when rendering help, so
* the same command renders correctly under any product (bl / rag / …).
*/
usageArgs?: string;
options?: OptionDef[];
examples?: string[];
/**
* Example argument strings, each WITHOUT the `<bin> <path>` prefix
* (e.g. '--index-id idx_xxx --query "..."'). The runtime prepends
* `<bin> <path>` per product when rendering help.
*/
exampleArgs?: string[];
skipDefaultApiKeySetup?: boolean;
notes?: string[];
execute: (config: Config, flags: GlobalFlags) => Promise<void>;
}
export interface CommandSpec {
name: string;
description: string;
usage?: string;
/** See {@link Command.usageArgs} — argument portion only, no `<bin> <path>` prefix. */
usageArgs?: string;
options?: OptionDef[];
examples?: string[];
/** See {@link Command.exampleArgs} — argument strings only, no `<bin> <path>` prefix. */
exampleArgs?: string[];
skipDefaultApiKeySetup?: boolean;
notes?: string[];
run: (config: Config, flags: GlobalFlags) => Promise<void>;
@@ -32,11 +43,10 @@ export interface CommandSpec {
export function defineCommand(spec: CommandSpec): Command {
return {
name: spec.name,
description: spec.description,
usage: spec.usage,
usageArgs: spec.usageArgs,
options: spec.options,
examples: spec.examples,
exampleArgs: spec.exampleArgs,
skipDefaultApiKeySetup: spec.skipDefaultApiKeySetup,
notes: spec.notes,
execute: (config, flags) => spec.run(config, flags),
-1
View File
@@ -1,6 +1,5 @@
export { generateFilename } from "./filename.ts";
export { resolveOutputDir } from "./output-dir.ts";
export { generateToolSchema } from "./schema.ts";
export { maskToken } from "./token.ts";
export { isInteractive } from "./env.ts";
export { isCI } from "./env.ts";

Some files were not shown because too many files have changed in this diff Show More