Skip to content

Commit 1f56fea

Browse files
committed
refactor(commands): route all exits through the central error handler
Commands no longer call process.exit() directly. Every failure now throws UsageError (bad input → exit 2) or BailianError (runtime failure, with AUTH/TIMEOUT codes), so the runtime's handleError stays the single exit point and telemetry always flushes. - convert 27 process.exit() sites across 15 commands to throws - move cross-flag/value checks into validate(); use flag `choices` for --events / --sort; drop dead --model required check - keep pipeline's process.exitCode for lint-style soft failures - enforce with unicorn/no-process-exit, allowed only in runtime/tools/tests - fix incidental lint: void floating run(), narrow console errorCode, align generate-reference type imports to source
1 parent 2f9558c commit 1f56fea

24 files changed

Lines changed: 116 additions & 118 deletions

File tree

packages/cli/src/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { createCli } from "bailian-cli-runtime";
22
import { commands } from "./commands.ts";
33
import pkg from "../package.json" with { type: "json" };
44

5-
createCli(commands, {
5+
void createCli(commands, {
66
binName: "bl",
77
version: pkg.version,
88
clientName: "bailian-cli",

packages/cli/tests/e2e/pipeline.e2e.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,6 @@ describe("e2e: pipeline", () => {
245245
]);
246246
expect(exitCode).toBe(2);
247247
expect(stdout).toBe("");
248-
expect(stderr).toMatch(/unsupported --events format: bogus/i);
248+
expect(stderr).toMatch(/--events must be one of: jsonl/i);
249249
});
250250
});

packages/cli/tests/e2e/quota.e2e.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ describe("e2e: quota", () => {
5353

5454
test("quota check --period 0 报错最小值", async () => {
5555
const { stderr, exitCode } = await runCli(["quota", "check", "--period", "0.5"]);
56-
expect(exitCode).toBe(1);
56+
expect(exitCode).toBe(2);
5757
expect(stderr).toContain("at least 1 minute");
5858
});
5959
});
@@ -184,7 +184,7 @@ describe.skipIf(!isConsoleE2EReady())("e2e: quota(Console)", () => {
184184
"--tpm",
185185
"999",
186186
]);
187-
expect(exitCode).toBe(1);
187+
expect(exitCode).toBe(2);
188188
expect(stderr).toContain("out of range");
189189
expect(stderr).toContain("Current");
190190
expect(stderr).toContain("Range");

packages/commands/src/commands/app/call.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
defineCommand,
3+
UsageError,
34
appCompletionPath,
45
parseSSE,
56
detectOutputFormat,
@@ -114,8 +115,7 @@ export default defineCommand({
114115
try {
115116
body.input.biz_params = JSON.parse(flags.bizParams);
116117
} catch {
117-
process.stderr.write("Error: --biz-params must be valid JSON\n");
118-
process.exit(1);
118+
throw new UsageError("--biz-params must be valid JSON");
119119
}
120120
}
121121

packages/commands/src/commands/console/call.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { defineCommand, effectiveConsoleGatewayConfig, detectOutputFormat } from "bailian-cli-core";
1+
import {
2+
defineCommand,
3+
UsageError,
4+
effectiveConsoleGatewayConfig,
5+
detectOutputFormat,
6+
} from "bailian-cli-core";
27
import { emitResult } from "bailian-cli-runtime";
38

49
export default defineCommand({
@@ -39,8 +44,7 @@ export default defineCommand({
3944
try {
4045
data = JSON.parse(dataRaw) as Record<string, unknown>;
4146
} catch {
42-
process.stderr.write("Error: --data must be valid JSON\n");
43-
process.exit(1);
47+
throw new UsageError("--data must be valid JSON");
4448
}
4549

4650
const format = detectOutputFormat(config.output);

packages/commands/src/commands/mcp/call.ts

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
import { defineCommand, bailianMcpPath, detectOutputFormat } from "bailian-cli-core";
1+
import {
2+
defineCommand,
3+
UsageError,
4+
BailianError,
5+
bailianMcpPath,
6+
detectOutputFormat,
7+
} from "bailian-cli-core";
28
import { emitResult } from "bailian-cli-runtime";
39

410
function parseArgFlags(raw: string[]): Record<string, unknown> {
511
const out: Record<string, unknown> = {};
612
for (const item of raw) {
713
const idx = item.indexOf("=");
814
if (idx <= 0) {
9-
process.stderr.write(`Error: --arg must be in K=V form, got: ${item}\n`);
10-
process.exit(1);
15+
throw new UsageError(`--arg must be in K=V form, got: ${item}`);
1116
}
1217
const key = item.slice(0, idx).trim();
1318
const rawVal = item.slice(idx + 1);
@@ -64,25 +69,23 @@ export default defineCommand({
6469

6570
const dot = target.indexOf(".");
6671
if (dot <= 0 || dot === target.length - 1) {
67-
process.stderr.write(`Error: target must be <server-code>.<tool>, got "${target}".\n`);
68-
process.exit(1);
72+
throw new UsageError(`target must be <server-code>.<tool>, got "${target}".`);
6973
}
7074
const serverCode = target.slice(0, dot);
7175
const toolName = target.slice(dot + 1);
7276

7377
let toolArgs: Record<string, unknown> = {};
7478
if (flags.json) {
79+
let parsed: unknown;
7580
try {
76-
const parsed = JSON.parse(flags.json);
77-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
78-
process.stderr.write("Error: --json must decode to an object.\n");
79-
process.exit(1);
80-
}
81-
toolArgs = parsed as Record<string, unknown>;
81+
parsed = JSON.parse(flags.json);
8282
} catch (err) {
83-
process.stderr.write(`Error: --json is not valid JSON — ${(err as Error).message}\n`);
84-
process.exit(1);
83+
throw new UsageError(`--json is not valid JSON — ${(err as Error).message}`);
8584
}
85+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
86+
throw new UsageError("--json must decode to an object.");
87+
}
88+
toolArgs = parsed as Record<string, unknown>;
8689
}
8790
Object.assign(toolArgs, parseArgFlags(flags.arg ?? []));
8891
if (flags.query !== undefined) toolArgs.query = flags.query;
@@ -109,8 +112,7 @@ export default defineCommand({
109112

110113
if (result.isError) {
111114
const errText = result.content.map((c) => c.text || "").join("\n");
112-
process.stderr.write(`Tool error: ${errText}\n`);
113-
process.exit(1);
115+
throw new BailianError(`Tool error: ${errText}`);
114116
}
115117

116118
emitResult(result, format);

packages/commands/src/commands/memory/add.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
defineCommand,
3+
UsageError,
34
memoryAddPath,
45
detectOutputFormat,
56
type FlagsDef,
@@ -52,8 +53,7 @@ export default defineCommand({
5253
try {
5354
body.messages = JSON.parse(flags.messages);
5455
} catch {
55-
process.stderr.write("Error: --messages must be valid JSON array\n");
56-
process.exit(1);
56+
throw new UsageError("--messages must be valid JSON array");
5757
}
5858
}
5959

packages/commands/src/commands/memory/profile-create.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
defineCommand,
3+
UsageError,
34
profileSchemaPath,
45
detectOutputFormat,
56
type ProfileSchemaCreateRequest,
@@ -38,8 +39,7 @@ export default defineCommand({
3839
try {
3940
attributes = JSON.parse(attrStr);
4041
} catch {
41-
process.stderr.write("Error: --attributes must be valid JSON array\n");
42-
process.exit(1);
42+
throw new UsageError("--attributes must be valid JSON array");
4343
}
4444

4545
const body: ProfileSchemaCreateRequest = { name, attributes };

packages/commands/src/commands/memory/search.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
defineCommand,
3+
UsageError,
34
memorySearchPath,
45
detectOutputFormat,
56
type FlagsDef,
@@ -49,8 +50,7 @@ export default defineCommand({
4950
try {
5051
body.messages = JSON.parse(flags.messages);
5152
} catch {
52-
process.stderr.write("Error: --messages must be valid JSON array\n");
53-
process.exit(1);
53+
throw new UsageError("--messages must be valid JSON array");
5454
}
5555
}
5656

packages/commands/src/commands/pipeline/load-file.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { readFile } from "node:fs/promises";
22
import { extname } from "node:path";
3+
import { UsageError } from "bailian-cli-core";
34
import type { PipelineDefinition } from "bailian-cli-runtime";
45

56
export async function loadPipelineFile(filePath: string): Promise<PipelineDefinition> {
67
const raw = await readFile(filePath, "utf-8").catch((err: Error) => {
7-
process.stderr.write(`Error: cannot read pipeline file: ${err.message}\n`);
8-
process.exit(2);
8+
throw new UsageError(`cannot read pipeline file: ${err.message}`);
99
});
1010
const ext = extname(filePath).toLowerCase();
1111
let parsed: unknown;

0 commit comments

Comments
 (0)