Skip to content

Commit 180aac9

Browse files
committed
fix(managed-agent): show absolute YAML paths during init
1 parent aa14842 commit 180aac9

2 files changed

Lines changed: 115 additions & 2 deletions

File tree

packages/commands/src/commands/managed-agent/init.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { existsSync } from "node:fs";
22
import { readFile, writeFile } from "node:fs/promises";
3+
import { resolve } from "node:path";
34
import {
45
BailianError,
56
defineCommand,
@@ -87,7 +88,7 @@ export default defineCommand({
8788

8889
if (existsSync(file) && !flags.force) {
8990
throw new BailianError(
90-
`${file} already exists.`,
91+
`${resolve(file)} already exists.`,
9192
ExitCode.USAGE,
9293
"Pass --force to overwrite.",
9394
);
@@ -128,7 +129,7 @@ export default defineCommand({
128129
if (format === "json") {
129130
emitResult({ created: file, provider: "bailian", agent: agentName }, format);
130131
} else {
131-
emitBare(`Created ${file}`);
132+
emitBare(`Created ${resolve(file)}`);
132133
emitBare(
133134
"Credentials: run `bl auth login --api-key <key> --base-url <url>`, or set DASHSCOPE_API_KEY / BAILIAN_BASE_URL.",
134135
);
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { mkdir, mkdtemp, readFile, realpath, rm, stat, writeFile } from "node:fs/promises";
2+
import { tmpdir } from "node:os";
3+
import { dirname, join } from "node:path";
4+
import { fileURLToPath } from "node:url";
5+
import { runNodeMain } from "e2e/runner";
6+
import { afterEach, describe, expect, test } from "vite-plus/test";
7+
import { parseStdoutJson } from "./helpers.ts";
8+
9+
const directories: string[] = [];
10+
11+
async function temporaryDirectory() {
12+
const directory = await realpath(await mkdtemp(join(tmpdir(), "bailian-yaml-init-")));
13+
directories.push(directory);
14+
return directory;
15+
}
16+
17+
afterEach(async () => {
18+
for (const directory of directories.splice(0)) {
19+
await rm(directory, { recursive: true, force: true });
20+
}
21+
});
22+
23+
async function runInit(directory: string, args: string[] = []) {
24+
return runNodeMain(
25+
fileURLToPath(new URL("./harness/main.ts", import.meta.url)),
26+
["managed-agent", "init", ...args],
27+
{
28+
cwd: directory,
29+
env: {
30+
BAILIAN_CONFIG_DIR: await temporaryDirectory(),
31+
BAILIAN_E2E_ROUTES: JSON.stringify([
32+
{ path: "managed-agent init", export: "managedAgentInit" },
33+
]),
34+
},
35+
},
36+
);
37+
}
38+
39+
describe("e2e: managed-agent init output path", () => {
40+
test.each(["default", "relative", "absolute"] as const)(
41+
"reports the absolute YAML path for a %s output path",
42+
async (pathKind) => {
43+
const directory = await temporaryDirectory();
44+
const relativePath =
45+
pathKind === "default" ? "agents.yaml" : join("config files", "custom agents.yaml");
46+
const outputPath = join(directory, relativePath);
47+
await mkdir(dirname(outputPath), { recursive: true });
48+
const args =
49+
pathKind === "default"
50+
? []
51+
: ["--file", pathKind === "absolute" ? outputPath : relativePath];
52+
53+
const result = await runInit(directory, args);
54+
55+
expect(result.exitCode, result.stderr).toBe(0);
56+
expect(result.stdout.split(/\r?\n/)[0]).toBe(`Created ${outputPath}`);
57+
expect(await readFile(outputPath, "utf8")).toContain("agents:");
58+
},
59+
);
60+
61+
test("preserves the JSON output contract", async () => {
62+
const directory = await temporaryDirectory();
63+
const result = await runInit(directory, ["--file", "custom.yaml", "--output", "json"]);
64+
65+
expect(result.exitCode, result.stderr).toBe(0);
66+
expect(parseStdoutJson(result.stdout)).toEqual({
67+
created: "custom.yaml",
68+
provider: "bailian",
69+
agent: "assistant",
70+
});
71+
expect((await stat(join(directory, "custom.yaml"))).isFile()).toBe(true);
72+
});
73+
74+
test("keeps dry-run read-only without reporting a created file", async () => {
75+
const directory = await temporaryDirectory();
76+
const result = await runInit(directory, ["--dry-run", "--output", "json"]);
77+
78+
expect(result.exitCode, result.stderr).toBe(0);
79+
expect(parseStdoutJson(result.stdout)).toEqual({
80+
would_create: "agents.yaml",
81+
provider: "bailian",
82+
agent: "assistant",
83+
would_update_gitignore: true,
84+
});
85+
expect(await stat(join(directory, "agents.yaml")).catch(() => null)).toBeNull();
86+
expect(await stat(join(directory, ".gitignore")).catch(() => null)).toBeNull();
87+
});
88+
89+
test.each(["default", "relative", "absolute"] as const)(
90+
"reports the absolute existing %s path without overwriting the YAML",
91+
async (pathKind) => {
92+
const directory = await temporaryDirectory();
93+
const relativePath =
94+
pathKind === "default" ? "agents.yaml" : join("config files", "custom agents.yaml");
95+
const outputPath = join(directory, relativePath);
96+
await mkdir(dirname(outputPath), { recursive: true });
97+
await writeFile(outputPath, "Keep user configuration.\n");
98+
const args =
99+
pathKind === "default"
100+
? []
101+
: ["--file", pathKind === "absolute" ? outputPath : relativePath];
102+
103+
const result = await runInit(directory, args);
104+
105+
expect(result.exitCode).toBe(2);
106+
expect(result.stderr).toContain(`${outputPath} already exists.`);
107+
expect(result.stderr).toContain("Pass --force to overwrite.");
108+
expect(result.stdout).not.toContain("Created ");
109+
expect(await readFile(outputPath, "utf8")).toBe("Keep user configuration.\n");
110+
},
111+
);
112+
});

0 commit comments

Comments
 (0)