Skip to content

Commit d971a04

Browse files
committed
refactor(cli): split into runtime / commands packages for composable CLIs
Decompose the monolithic `cli` package into three layers so multiple products can be assembled from a shared base: - bailian-cli-runtime: framework infra (createCli, registry, args, output, pipeline, utils) — product-agnostic - bailian-cli-commands: command library, grouped (base/knowledge/text/ media/memory/misc) so each product picks the sets it needs - packages/cli (bl): full command set; packages/rag (rag): base + knowledge only Product identity (binName / clientName / npmPackage) is injected at the createCli boundary and required there, with no per-consumer defaults.
1 parent 1ffcbdd commit d971a04

130 files changed

Lines changed: 1105 additions & 407 deletions

File tree

Some content is hidden

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

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"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",
2020
"dev": "pnpm -F bailian-cli-core dev",
2121
"bl": "pnpm -F bailian-cli dev",
22+
"rag": "pnpm -F bailian-cli-rag dev",
2223
"test": "vp test",
2324
"release:check": "node tools/release/check.mjs",
2425
"wiki:crawl": "node tools/wiki-crawler/index.mjs",

packages/cli/package.json

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,32 +44,23 @@
4444
"check": "vp check"
4545
},
4646
"dependencies": {
47+
"bailian-cli-commands": "workspace:*",
4748
"bailian-cli-core": "workspace:*",
48-
"boxen": "catalog:",
49-
"chalk": "catalog:",
50-
"undici": "catalog:"
49+
"bailian-cli-runtime": "workspace:*"
5150
},
5251
"devDependencies": {
5352
"@clack/prompts": "^0.7.0",
5453
"@types/node": "catalog:",
5554
"@typescript/native-preview": "7.0.0-dev.20260328.1",
5655
"ajv": "catalog:",
56+
"boxen": "catalog:",
57+
"chalk": "catalog:",
5758
"typescript": "^6.0.2",
58-
"vite-plus": "catalog:",
59+
"undici": "catalog:",
60+
"vite-plus": "0.1.22",
5961
"yaml": "catalog:"
6062
},
6163
"engines": {
6264
"node": ">=22.12.0"
63-
},
64-
"inlinedDependencies": {
65-
"@clack/core": "0.3.5",
66-
"@clack/prompts": "0.7.0",
67-
"ajv": "8.20.0",
68-
"fast-deep-equal": "3.1.3",
69-
"fast-uri": "3.1.2",
70-
"json-schema-traverse": "1.0.0",
71-
"picocolors": "1.1.1",
72-
"sisteransi": "1.0.5",
73-
"yaml": "2.8.3"
7465
}
7566
}

packages/cli/src/main.ts

Lines changed: 11 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -1,151 +1,11 @@
1-
import { scanCommandPath, parseFlags } from "./args.ts";
2-
import { registry } from "./registry.ts";
3-
import {
4-
GLOBAL_OPTIONS,
5-
loadConfig,
6-
resolveCredential,
7-
trackCommandExecution,
8-
flushTelemetry,
9-
} from "bailian-cli-core";
10-
import { ensureApiKey } from "./utils/ensure-key.ts";
11-
import { setupProxyFromEnv } from "./proxy.ts";
12-
import { handleError } from "./error-handler.ts";
13-
import { checkForUpdate, getPendingUpdateNotification } from "./utils/update-checker.ts";
14-
import { maybeShowStatusBar } from "./output/status-bar.ts";
15-
import { printWelcomeBanner, printQuickStart } from "./output/banner.ts";
16-
import { CLI_VERSION } from "./version.ts";
17-
import {
18-
printCurrentCommandHelp,
19-
registerCommandHelpPrinter,
20-
setExecutingCommandPath,
21-
} from "./utils/command-help.ts";
22-
23-
// 必须在任何 fetch 发起前安装(含 update-checker / telemetry)
24-
try {
25-
setupProxyFromEnv();
26-
} catch (err) {
27-
handleError(err);
28-
}
29-
30-
registerCommandHelpPrinter((commandPath, out) => {
31-
registry.printHelp(commandPath, out);
32-
});
33-
34-
// 优雅处理 Ctrl+C
35-
// 退出前尝试 best-effort 刷出埋点,让去抖队列中 / 在途的 fetch 请求有机会
36-
// 落网络;flush 与较短超时 race,保证 SIGINT 仍然响应及时。
37-
process.on("SIGINT", () => {
38-
process.stderr.write("\nInterrupted. Exiting.\n");
39-
void flushTelemetry(500).finally(() => process.exit(130));
40-
});
41-
42-
// 优雅处理 stdout EPIPE(例如管道到提前退出的 `mpv`)
43-
process.stdout.on("error", (e: NodeJS.ErrnoException) => {
44-
if (e.code === "EPIPE") process.exit(0);
45-
else throw e;
46-
});
47-
48-
async function main() {
49-
let argv = process.argv.slice(2);
50-
if (argv[0] === "--") argv = argv.slice(1);
51-
52-
if (argv.includes("--version") || argv.includes("-v")) {
53-
process.stdout.write(`bl ${CLI_VERSION}\n`);
54-
process.exit(0);
55-
}
56-
57-
const commandPath = scanCommandPath(argv, GLOBAL_OPTIONS);
58-
59-
if (argv.includes("--help") || argv.includes("-h")) {
60-
registry.printHelp(commandPath, process.stderr);
61-
process.exit(0);
62-
}
63-
64-
// 未传任何命令:展示帮助信息与登录引导
65-
if (commandPath.length === 0) {
66-
registry.printHelp([], process.stderr);
67-
68-
const flags = parseFlags(argv, GLOBAL_OPTIONS);
69-
const config = loadConfig(flags);
70-
config.clientName = "bailian-cli";
71-
config.clientVersion = CLI_VERSION;
72-
73-
const hasKey = !!(
74-
config.apiKey ||
75-
config.fileApiKey ||
76-
config.fileAccessToken ||
77-
config.accessTokenEnv
78-
);
79-
if (hasKey) printQuickStart();
80-
else printWelcomeBanner();
81-
process.exit(0);
82-
}
83-
84-
// 组路径(例如 `bl speech` 未接子命令):展示帮助后干净退出
85-
if (registry.isGroupPath(commandPath)) {
86-
registry.printHelp(commandPath, process.stderr);
87-
process.exit(0);
88-
}
89-
90-
const { command, extra } = registry.resolve(commandPath);
91-
const flags = parseFlags(argv, [...GLOBAL_OPTIONS, ...(command.options ?? [])]);
92-
93-
if (extra.length > 0) (flags as Record<string, unknown>)._positional = extra;
94-
95-
const config = loadConfig(flags);
96-
config.clientName = "bailian-cli";
97-
config.clientVersion = CLI_VERSION;
98-
99-
// 默认执行 ensureApiKey;自行处理鉴权或仅需 Console/AK-SK 等的命令在 defineCommand 上设 skipDefaultApiKeySetup
100-
if (!command.skipDefaultApiKeySetup) {
101-
await ensureApiKey(config);
102-
try {
103-
const credential = await resolveCredential(config);
104-
maybeShowStatusBar(config, credential.token, credential);
105-
} catch {
106-
/* 没有凭证,不展示状态栏 */
107-
}
108-
}
109-
110-
const updateCheckPromise = checkForUpdate(CLI_VERSION).catch(() => {});
111-
112-
setExecutingCommandPath(commandPath);
113-
114-
if (
115-
commandPath[0] === "auth" &&
116-
commandPath[1] === "login" &&
117-
!flags.console &&
118-
!String((flags.apiKey as string | undefined) ?? "").trim() &&
119-
!String(config.apiKey ?? "").trim() &&
120-
!process.env.DASHSCOPE_API_KEY?.trim()
121-
) {
122-
printCurrentCommandHelp(process.stderr);
123-
process.exit(0);
124-
}
125-
126-
await trackCommandExecution(config, commandPath, flags, () => command.execute(config, flags));
127-
128-
await updateCheckPromise;
129-
const isUpdateCommand = commandPath.length === 1 && commandPath[0] === "update";
130-
const newVersion = getPendingUpdateNotification();
131-
if (newVersion && !config.quiet && !isUpdateCommand) {
132-
const isTTY = process.stderr.isTTY;
133-
const yellow = isTTY ? "\x1b[33m" : "";
134-
const cyan = isTTY ? "\x1b[36m" : "";
135-
const reset = isTTY ? "\x1b[0m" : "";
136-
process.stderr.write(`\n ${yellow}Update available: ${CLI_VERSION}${newVersion}${reset}\n`);
137-
process.stderr.write(` Run ${cyan}bl update${reset} to upgrade\n\n`);
138-
}
139-
140-
// 进程退出前尽力等待在途的埋点完成。
141-
// 使用较短超时兜底,避免慢网拖慢用户感知。
142-
await flushTelemetry(1000);
143-
}
144-
145-
main().catch((err) => {
146-
// 在 handleError() 调用 process.exit() 之前刷出在途埋点。
147-
// 命令抛出的错误已被 trackCommandExecution 的 finally 块记录,
148-
// 但底层 tracker 有 ~500ms 的发送去抖。不主动 flush 的话,
149-
// 错误事件会随进程退出丢掉。
150-
void flushTelemetry(1000).finally(() => handleError(err));
151-
});
1+
import { createCli } from "bailian-cli-runtime";
2+
import { commands } from "bailian-cli-commands";
3+
import pkg from "../package.json" with { type: "json" };
4+
5+
// Full bailian-cli product: every command, exposed under the `bl` binary.
6+
createCli(commands, {
7+
binName: "bl",
8+
version: pkg.version,
9+
clientName: "bailian-cli",
10+
npmPackage: "bailian-cli",
11+
}).run();

packages/commands/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
dist
3+
*.log
4+
.DS_Store

packages/commands/package.json

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{
2+
"name": "bailian-cli-commands",
3+
"version": "1.4.0",
4+
"description": "Command library for bailian-cli products (knowledge, memory, media, …). See https://www.npmjs.com/package/bailian-cli for usage.",
5+
"homepage": "https://bailian.console.aliyun.com/cli",
6+
"bugs": {
7+
"url": "https://github.com/modelstudioai/cli/issues"
8+
},
9+
"license": "Apache-2.0",
10+
"author": "Aliyun Model Studio",
11+
"repository": {
12+
"type": "git",
13+
"url": "git+https://github.com/modelstudioai/cli.git",
14+
"directory": "packages/commands"
15+
},
16+
"files": [
17+
"dist"
18+
],
19+
"type": "module",
20+
"types": "./dist/index.d.mts",
21+
"exports": {
22+
".": "./dist/index.mjs",
23+
"./package.json": "./package.json"
24+
},
25+
"publishConfig": {
26+
"access": "public",
27+
"registry": "https://registry.npmjs.org/"
28+
},
29+
"scripts": {
30+
"build": "vp pack",
31+
"dev": "vp pack --watch",
32+
"test": "vp test",
33+
"check": "vp check"
34+
},
35+
"dependencies": {
36+
"bailian-cli-core": "workspace:*",
37+
"bailian-cli-runtime": "workspace:*",
38+
"boxen": "catalog:",
39+
"chalk": "catalog:",
40+
"yaml": "catalog:"
41+
},
42+
"devDependencies": {
43+
"@types/node": "catalog:",
44+
"@typescript/native-preview": "7.0.0-dev.20260328.1",
45+
"typescript": "^6.0.2",
46+
"vite-plus": "0.1.22"
47+
},
48+
"engines": {
49+
"node": ">=22.12.0"
50+
}
51+
}

packages/cli/src/commands/advisor/recommend.ts renamed to packages/commands/src/commands/advisor/recommend.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ import {
1717
} from "bailian-cli-core";
1818
import boxen from "boxen";
1919
import chalk, { Chalk, type ChalkInstance } from "chalk";
20-
import { emitBare, emitResult } from "../../output/output.ts";
21-
import { createSpinner } from "../../output/progress.ts";
22-
import { failIfMissing, promptText } from "../../output/prompt.ts";
20+
import { emitBare, emitResult } from "bailian-cli-runtime";
21+
import { createSpinner } from "bailian-cli-runtime";
22+
import { failIfMissing, promptText } from "bailian-cli-runtime";
2323

2424
function formatContextWindow(tokens: number): string {
2525
if (tokens >= 1_000_000)

packages/cli/src/commands/app/call.ts renamed to packages/commands/src/commands/app/call.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import {
1111
type AppStreamChunk,
1212
type AppCompletionResponse,
1313
} from "bailian-cli-core";
14-
import { failIfMissing } from "../../output/prompt.ts";
15-
import { emitResult, emitBare } from "../../output/output.ts";
14+
import { failIfMissing } from "bailian-cli-runtime";
15+
import { emitResult, emitBare } from "bailian-cli-runtime";
1616

1717
export default defineCommand({
1818
name: "app call",

packages/cli/src/commands/app/list.ts renamed to packages/commands/src/commands/app/list.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
type Config,
77
type GlobalFlags,
88
} from "bailian-cli-core";
9-
import { emitResult } from "../../output/output.ts";
9+
import { emitResult } from "bailian-cli-runtime";
1010

1111
const APP_LIST_API = "zeldaEasy.broadscope-bailian.app-control.list";
1212

File renamed without changes.

packages/cli/src/commands/auth/login.ts renamed to packages/commands/src/commands/auth/login.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import {
77
type Config,
88
type GlobalFlags,
99
} from "bailian-cli-core";
10-
import { printQuickStart } from "../../output/banner.ts";
11-
import { emitBare } from "../../output/output.ts";
12-
import { promptConfirm } from "../../output/prompt.ts";
13-
import { printCurrentCommandHelp } from "../../utils/command-help.ts";
10+
import { printQuickStart } from "bailian-cli-runtime";
11+
import { emitBare } from "bailian-cli-runtime";
12+
import { promptConfirm } from "bailian-cli-runtime";
13+
import { printCurrentCommandHelp } from "bailian-cli-runtime";
1414
import {
1515
resolveConsoleOrigin,
1616
runConsoleLogin,

0 commit comments

Comments
 (0)