From 180aac9f28b2bc4d09e35ec6bb79d09becc406c4 Mon Sep 17 00:00:00 2001 From: chenanran555 Date: Tue, 8 Sep 2026 16:57:15 +0800 Subject: [PATCH] fix(managed-agent): show absolute YAML paths during init --- .../src/commands/managed-agent/init.ts | 5 +- .../tests/e2e/managed-agent-init.e2e.test.ts | 112 ++++++++++++++++++ 2 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 packages/commands/tests/e2e/managed-agent-init.e2e.test.ts diff --git a/packages/commands/src/commands/managed-agent/init.ts b/packages/commands/src/commands/managed-agent/init.ts index bd6ac71..210cbf2 100644 --- a/packages/commands/src/commands/managed-agent/init.ts +++ b/packages/commands/src/commands/managed-agent/init.ts @@ -1,5 +1,6 @@ import { existsSync } from "node:fs"; import { readFile, writeFile } from "node:fs/promises"; +import { resolve } from "node:path"; import { BailianError, defineCommand, @@ -87,7 +88,7 @@ export default defineCommand({ if (existsSync(file) && !flags.force) { throw new BailianError( - `${file} already exists.`, + `${resolve(file)} already exists.`, ExitCode.USAGE, "Pass --force to overwrite.", ); @@ -128,7 +129,7 @@ export default defineCommand({ if (format === "json") { emitResult({ created: file, provider: "bailian", agent: agentName }, format); } else { - emitBare(`Created ${file}`); + emitBare(`Created ${resolve(file)}`); emitBare( "Credentials: run `bl auth login --api-key --base-url `, or set DASHSCOPE_API_KEY / BAILIAN_BASE_URL.", ); diff --git a/packages/commands/tests/e2e/managed-agent-init.e2e.test.ts b/packages/commands/tests/e2e/managed-agent-init.e2e.test.ts new file mode 100644 index 0000000..0f8daba --- /dev/null +++ b/packages/commands/tests/e2e/managed-agent-init.e2e.test.ts @@ -0,0 +1,112 @@ +import { mkdir, mkdtemp, readFile, realpath, rm, stat, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { runNodeMain } from "e2e/runner"; +import { afterEach, describe, expect, test } from "vite-plus/test"; +import { parseStdoutJson } from "./helpers.ts"; + +const directories: string[] = []; + +async function temporaryDirectory() { + const directory = await realpath(await mkdtemp(join(tmpdir(), "bailian-yaml-init-"))); + directories.push(directory); + return directory; +} + +afterEach(async () => { + for (const directory of directories.splice(0)) { + await rm(directory, { recursive: true, force: true }); + } +}); + +async function runInit(directory: string, args: string[] = []) { + return runNodeMain( + fileURLToPath(new URL("./harness/main.ts", import.meta.url)), + ["managed-agent", "init", ...args], + { + cwd: directory, + env: { + BAILIAN_CONFIG_DIR: await temporaryDirectory(), + BAILIAN_E2E_ROUTES: JSON.stringify([ + { path: "managed-agent init", export: "managedAgentInit" }, + ]), + }, + }, + ); +} + +describe("e2e: managed-agent init output path", () => { + test.each(["default", "relative", "absolute"] as const)( + "reports the absolute YAML path for a %s output path", + async (pathKind) => { + const directory = await temporaryDirectory(); + const relativePath = + pathKind === "default" ? "agents.yaml" : join("config files", "custom agents.yaml"); + const outputPath = join(directory, relativePath); + await mkdir(dirname(outputPath), { recursive: true }); + const args = + pathKind === "default" + ? [] + : ["--file", pathKind === "absolute" ? outputPath : relativePath]; + + const result = await runInit(directory, args); + + expect(result.exitCode, result.stderr).toBe(0); + expect(result.stdout.split(/\r?\n/)[0]).toBe(`Created ${outputPath}`); + expect(await readFile(outputPath, "utf8")).toContain("agents:"); + }, + ); + + test("preserves the JSON output contract", async () => { + const directory = await temporaryDirectory(); + const result = await runInit(directory, ["--file", "custom.yaml", "--output", "json"]); + + expect(result.exitCode, result.stderr).toBe(0); + expect(parseStdoutJson(result.stdout)).toEqual({ + created: "custom.yaml", + provider: "bailian", + agent: "assistant", + }); + expect((await stat(join(directory, "custom.yaml"))).isFile()).toBe(true); + }); + + test("keeps dry-run read-only without reporting a created file", async () => { + const directory = await temporaryDirectory(); + const result = await runInit(directory, ["--dry-run", "--output", "json"]); + + expect(result.exitCode, result.stderr).toBe(0); + expect(parseStdoutJson(result.stdout)).toEqual({ + would_create: "agents.yaml", + provider: "bailian", + agent: "assistant", + would_update_gitignore: true, + }); + expect(await stat(join(directory, "agents.yaml")).catch(() => null)).toBeNull(); + expect(await stat(join(directory, ".gitignore")).catch(() => null)).toBeNull(); + }); + + test.each(["default", "relative", "absolute"] as const)( + "reports the absolute existing %s path without overwriting the YAML", + async (pathKind) => { + const directory = await temporaryDirectory(); + const relativePath = + pathKind === "default" ? "agents.yaml" : join("config files", "custom agents.yaml"); + const outputPath = join(directory, relativePath); + await mkdir(dirname(outputPath), { recursive: true }); + await writeFile(outputPath, "Keep user configuration.\n"); + const args = + pathKind === "default" + ? [] + : ["--file", pathKind === "absolute" ? outputPath : relativePath]; + + const result = await runInit(directory, args); + + expect(result.exitCode).toBe(2); + expect(result.stderr).toContain(`${outputPath} already exists.`); + expect(result.stderr).toContain("Pass --force to overwrite."); + expect(result.stdout).not.toContain("Created "); + expect(await readFile(outputPath, "utf8")).toBe("Keep user configuration.\n"); + }, + ); +});