Skip to content

Commit 1dfb440

Browse files
committed
Merge branch 'main' of github.com:modelstudioai/cli into feat/global-watermark-config
2 parents fd2077b + 2090293 commit 1dfb440

23 files changed

Lines changed: 489 additions & 53 deletions

File tree

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,23 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
66

77
[中文版](CHANGELOG.zh.md) · [README](README.md) · [Contributing](CONTRIBUTING.md)
88

9+
## [1.22.0] - 2026-09-08
10+
11+
### Changed
12+
13+
- **Project initialization**`managed-agent project init` now creates `./managed-agent` by default. Use `--project .` to initialize in place. **(BREAKING)**
14+
- **Build confirmation**`managed-agent project build` no longer requires confirmation and rejects `--yes`. Use `--dry-run` for a read-only preview; Publish still requires confirmation. **(BREAKING)**
15+
- **Managed Agent SDK** — upgrade to `0.7.1`. Build automatically associates active Agent-local resources while preserving explicit bindings, Skill versions, and File mount paths. Ambiguous Environment or Vault selections are rejected before writing.
16+
17+
### Fixed
18+
19+
- **Project diagnostics** — provide actionable project-root guidance and surface the underlying Build validation error.
20+
- **YAML initialization paths** — show the absolute YAML path in creation messages and existing-file errors.
21+
22+
### Internal
23+
24+
- Expand project initialization and Build regression coverage, and remove the obsolete Build confirmation flag from the local lifecycle E2E test.
25+
926
## [1.21.0] - 2026-09-07
1027

1128
### Added

CHANGELOG.zh.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,23 @@
66

77
[English](CHANGELOG.md) · [README](README.zh.md) · [参与贡献](CONTRIBUTING.zh.md)
88

9+
## [1.22.0] - 2026-09-08
10+
11+
### 变更
12+
13+
- **项目初始化** —— `managed-agent project init` 默认创建 `./managed-agent` 子目录;如需原地初始化,请使用 `--project .`**(BREAKING)**
14+
- **Build 确认机制** —— `managed-agent project build` 无需确认,并且不再接受 `--yes`。使用 `--dry-run` 可只读预览;Publish 仍需显式确认。**(BREAKING)**
15+
- **Managed Agent SDK** —— 升级至 `0.7.1`。Build 自动关联 Agent 目录下已启用的资源,保留显式引用、Skill 版本和 File 挂载路径;Environment 或 Vault 选择存在歧义时,在写入前报错。
16+
17+
### 修复
18+
19+
- **项目诊断** —— 提供可操作的项目根目录提示,并展示 Build 校验失败的具体原因。
20+
- **YAML 初始化路径** —— 创建成功及文件已存在的错误信息均展示 YAML 绝对路径。
21+
22+
### 内部
23+
24+
- 补充项目初始化和 Build 回归覆盖,移除本地闭环 E2E 测试中过时的 Build 确认参数。
25+
926
## [1.21.0] - 2026-09-07
1027

1128
### 新增

docs/agents/command-flag-change.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
- 类型由 `ParsedFlags<typeof FLAGS>` 推导;避免手写 `flags.x as number` 这类断言
2020
- 单 flag 必填用 `required: true`;跨 flag / 值相关校验放 `validate`
2121
- 默认值 fallback 写在命令实现或 `Settings` 解析层,不要重复解析 env/config
22+
- 需要在高风险确认前检查本地路径时,可用异步 `validate`;runtime 会在鉴权和确认前等待它完成。这里只允许本地只读检查,不写文件、不请求远端。非缺参的环境错误应抛出 `BailianError`,避免裸命令调用被当成缺参而仅显示 help。
2223

2324
### B. 鉴权 / 全局选项
2425

packages/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bailian-cli",
3-
"version": "1.21.0",
3+
"version": "1.22.0",
44
"description": "CLI for Aliyun Model Studio (DashScope) AI Platform.",
55
"keywords": [
66
"agent",

packages/commands/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bailian-cli-commands",
3-
"version": "1.21.0",
3+
"version": "1.22.0",
44
"description": "Command library for bailian-cli products (knowledge, memory, media, …). See https://www.npmjs.com/package/bailian-cli for usage.",
55
"homepage": "https://bailian.console.aliyun.com/cli",
66
"bugs": {
@@ -40,7 +40,7 @@
4040
"check": "vp check"
4141
},
4242
"dependencies": {
43-
"@openagentpack/sdk": "0.7.0",
43+
"@openagentpack/sdk": "0.7.1",
4444
"bailian-cli-core": "workspace:*",
4545
"bailian-cli-runtime": "workspace:*",
4646
"boxen": "catalog:",

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
);

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

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { stat } from "node:fs/promises";
2+
import { join } from "node:path";
13
import {
24
BailianError,
35
defineCommand,
@@ -14,6 +16,7 @@ import {
1416
planProjectPublish,
1517
previewProjectBuild,
1618
type ProjectBuildResolver,
19+
resolveDirectoryProjectRoot,
1720
validateDirectoryProject,
1821
} from "@openagentpack/sdk/project-workspace";
1922
import { CREDENTIALS_NOTE, resolveAgentProjectConfig } from "./_engine/config-loader.ts";
@@ -44,9 +47,23 @@ export const managedAgentProjectInit = defineCommand({
4447
},
4548
auth: "none",
4649
usageArgs: "[--project <directory>]",
47-
flags: PROJECT_FLAG,
48-
exampleArgs: ["", "--project ./my-agent"],
50+
flags: {
51+
project: {
52+
...PROJECT_FLAG.project,
53+
description: {
54+
"en-US": "Directory project root (default: ./managed-agent under the current directory)",
55+
"zh-CN": "目录项目根路径(默认:当前目录下的 ./managed-agent)",
56+
},
57+
},
58+
},
59+
exampleArgs: ["", "--project ./my-agent", "--project ."],
4960
notes: [
61+
{
62+
"en-US":
63+
"Without --project, creates a managed-agent/ subdirectory. Enter it before running other project commands. Use --project . to initialize in place or convert the current agents.yaml; existing project files are not overwritten.",
64+
"zh-CN":
65+
"不传 --project 时创建 managed-agent/ 子目录;后续项目操作请先进入该目录。使用 --project . 可在当前目录初始化或转换 agents.yaml;不会覆盖已有项目文件。",
66+
},
5067
{
5168
"en-US":
5269
"New projects include Skill, File, Vault, and Environment examples under each resource directory's _examples/. They are not referenced by agent.json and are excluded from Build/Publish. Copy an example outside _examples/ to enable it, then configure its Agent reference.",
@@ -55,16 +72,17 @@ export const managedAgentProjectInit = defineCommand({
5572
},
5673
],
5774
async run(ctx) {
75+
const projectRoot = ctx.flags.project ?? "./managed-agent";
5876
if (ctx.settings.dryRun) {
5977
emitResult(
6078
{
61-
would_initialize_project: ctx.flags.project ?? ".",
79+
would_initialize_project: projectRoot,
6280
},
6381
detectOutputFormat(ctx.settings.output),
6482
);
6583
return;
6684
}
67-
const result = await initializeDirectoryProject({ projectRoot: ctx.flags.project ?? "." });
85+
const result = await initializeDirectoryProject({ projectRoot });
6886
emitResult(result, detectOutputFormat(ctx.settings.output));
6987
},
7088
});
@@ -99,18 +117,34 @@ export const managedAgentProjectBuild = defineCommand({
99117
"zh-CN": "整理目录源文件并生成不可变的发布 Build",
100118
},
101119
auth: "none",
102-
risk: {
103-
level: "high",
104-
message: {
120+
notes: [
121+
{
105122
"en-US":
106-
"This organizes project source, moves literal Vault secrets into the local .env, and writes the previewed immutable Build.",
123+
"Build writes local project files without confirmation, including inferred resource associations and migration of plaintext Vault secrets into .env. Use --dry-run to preview without writing. Publish still requires explicit confirmation before remote changes.",
107124
"zh-CN":
108-
"该操作会整理项目源文件,将 Vault 明文密钥移入本地 .env,并写入已预览的不可变 Build。",
125+
"Build 无需确认即可写入本地项目文件,包括推断的资源关联及将 Vault 明文密钥移入 .env。使用 --dry-run 可只预览不写入。Publish 变更远端资源前仍需显式确认。",
109126
},
110-
},
127+
],
111128
usageArgs: "[--project <directory>]",
112129
flags: PROJECT_FLAG,
113-
exampleArgs: ["--dry-run", "--yes", "--project ./my-agent --yes"],
130+
exampleArgs: ["", "--dry-run", "--project ./my-agent"],
131+
async validate(flags) {
132+
await withAgentErrors(async () => {
133+
const root = await resolveDirectoryProjectRoot(flags.project ?? ".");
134+
const metadata = await stat(join(root, "project.json")).catch((error: unknown) => {
135+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
136+
throw error;
137+
});
138+
if (!metadata?.isFile()) {
139+
throw new BailianError(
140+
`Not a project root: ${root} (project.json is missing).`,
141+
ExitCode.USAGE,
142+
"Run from the directory containing project.json, or pass --project <directory>.",
143+
);
144+
}
145+
});
146+
return undefined;
147+
},
114148
async run(ctx) {
115149
const root = ctx.flags.project ?? ".";
116150
const preview = await previewProjectBuild(root);
@@ -120,7 +154,11 @@ export const managedAgentProjectBuild = defineCommand({
120154
return;
121155
}
122156
if (!preview.can_build)
123-
throw new BailianError("Directory project is invalid and cannot be built.", ExitCode.GENERAL);
157+
throw new BailianError(
158+
preview.diagnostics.find((diagnostic) => diagnostic.severity === "error")?.message ??
159+
"Directory project is invalid and cannot be built.",
160+
ExitCode.GENERAL,
161+
);
124162
const built = await commitProjectBuild({
125163
projectRoot: root,
126164
baseRevision: preview.project_revision,
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)