Compare commits

..

6 Commits

Author SHA1 Message Date
chenanran555 2090293f85 Merge pull request #192 from modelstudioai/feat/managed-agent-ci
fix(managed-agent): improve project initialization and build validation
2026-09-08 19:39:03 +08:00
chenanran555 a245359792 chore(release): prepare 1.22.0 2026-09-08 19:26:17 +08:00
chenanran555 19bd5a5ea8 test(managed-agent): remove obsolete build confirmation flag 2026-09-08 19:01:47 +08:00
chenanran555 e262f2e574 chore(deps): bump OpenAgentPack SDK to 0.7.1 2026-09-08 18:01:41 +08:00
chenanran555 180aac9f28 fix(managed-agent): show absolute YAML paths during init 2026-09-08 16:57:15 +08:00
chenanran555 aa1484264e fix(managed-agent): improve project initialization and build validation 2026-09-08 14:46:56 +08:00
23 changed files with 491 additions and 55 deletions
+17
View File
@@ -6,6 +6,23 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
[中文版](CHANGELOG.zh.md) · [README](README.md) · [Contributing](CONTRIBUTING.md)
## [1.22.0] - 2026-09-08
### Changed
- **Project initialization** — `managed-agent project init` now creates `./managed-agent` by default. Use `--project .` to initialize in place. **(BREAKING)**
- **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)**
- **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.
### Fixed
- **Project diagnostics** — provide actionable project-root guidance and surface the underlying Build validation error.
- **YAML initialization paths** — show the absolute YAML path in creation messages and existing-file errors.
### Internal
- Expand project initialization and Build regression coverage, and remove the obsolete Build confirmation flag from the local lifecycle E2E test.
## [1.21.0] - 2026-09-07
### Added
+17
View File
@@ -6,6 +6,23 @@
[English](CHANGELOG.md) · [README](README.zh.md) · [参与贡献](CONTRIBUTING.zh.md)
## [1.22.0] - 2026-09-08
### 变更
- **项目初始化** —— `managed-agent project init` 默认创建 `./managed-agent` 子目录;如需原地初始化,请使用 `--project .`。**(BREAKING)**
- **Build 确认机制** —— `managed-agent project build` 无需确认,并且不再接受 `--yes`。使用 `--dry-run` 可只读预览Publish 仍需显式确认。**(BREAKING)**
- **Managed Agent SDK** —— 升级至 `0.7.1`。Build 自动关联 Agent 目录下已启用的资源保留显式引用、Skill 版本和 File 挂载路径Environment 或 Vault 选择存在歧义时,在写入前报错。
### 修复
- **项目诊断** —— 提供可操作的项目根目录提示,并展示 Build 校验失败的具体原因。
- **YAML 初始化路径** —— 创建成功及文件已存在的错误信息均展示 YAML 绝对路径。
### 内部
- 补充项目初始化和 Build 回归覆盖,移除本地闭环 E2E 测试中过时的 Build 确认参数。
## [1.21.0] - 2026-09-07
### 新增
+1
View File
@@ -19,6 +19,7 @@
- 类型由 `ParsedFlags<typeof FLAGS>` 推导;避免手写 `flags.x as number` 这类断言
- 单 flag 必填用 `required: true`;跨 flag / 值相关校验放 `validate`
- 默认值 fallback 写在命令实现或 `Settings` 解析层,不要重复解析 env/config
- 需要在高风险确认前检查本地路径时,可用异步 `validate`;runtime 会在鉴权和确认前等待它完成。这里只允许本地只读检查,不写文件、不请求远端。非缺参的环境错误应抛出 `BailianError`,避免裸命令调用被当成缺参而仅显示 help。
### B. 鉴权 / 全局选项
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "bailian-cli",
"version": "1.21.0",
"version": "1.22.0",
"description": "CLI for Aliyun Model Studio (DashScope) AI Platform.",
"keywords": [
"agent",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "bailian-cli-commands",
"version": "1.21.0",
"version": "1.22.0",
"description": "Command library for bailian-cli products (knowledge, memory, media, …). See https://www.npmjs.com/package/bailian-cli for usage.",
"homepage": "https://bailian.console.aliyun.com/cli",
"bugs": {
@@ -40,7 +40,7 @@
"check": "vp check"
},
"dependencies": {
"@openagentpack/sdk": "0.7.0",
"@openagentpack/sdk": "0.7.1",
"bailian-cli-core": "workspace:*",
"bailian-cli-runtime": "workspace:*",
"boxen": "catalog:",
@@ -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 <key> --base-url <url>`, or set DASHSCOPE_API_KEY / BAILIAN_BASE_URL.",
);
@@ -1,3 +1,5 @@
import { stat } from "node:fs/promises";
import { join } from "node:path";
import {
BailianError,
defineCommand,
@@ -14,6 +16,7 @@ import {
planProjectPublish,
previewProjectBuild,
type ProjectBuildResolver,
resolveDirectoryProjectRoot,
validateDirectoryProject,
} from "@openagentpack/sdk/project-workspace";
import { CREDENTIALS_NOTE, resolveAgentProjectConfig } from "./_engine/config-loader.ts";
@@ -44,9 +47,23 @@ export const managedAgentProjectInit = defineCommand({
},
auth: "none",
usageArgs: "[--project <directory>]",
flags: PROJECT_FLAG,
exampleArgs: ["", "--project ./my-agent"],
flags: {
project: {
...PROJECT_FLAG.project,
description: {
"en-US": "Directory project root (default: ./managed-agent under the current directory)",
"zh-CN": "目录项目根路径(默认:当前目录下的 ./managed-agent",
},
},
},
exampleArgs: ["", "--project ./my-agent", "--project ."],
notes: [
{
"en-US":
"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.",
"zh-CN":
"不传 --project 时创建 managed-agent/ 子目录;后续项目操作请先进入该目录。使用 --project . 可在当前目录初始化或转换 agents.yaml不会覆盖已有项目文件。",
},
{
"en-US":
"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({
},
],
async run(ctx) {
const projectRoot = ctx.flags.project ?? "./managed-agent";
if (ctx.settings.dryRun) {
emitResult(
{
would_initialize_project: ctx.flags.project ?? ".",
would_initialize_project: projectRoot,
},
detectOutputFormat(ctx.settings.output),
);
return;
}
const result = await initializeDirectoryProject({ projectRoot: ctx.flags.project ?? "." });
const result = await initializeDirectoryProject({ projectRoot });
emitResult(result, detectOutputFormat(ctx.settings.output));
},
});
@@ -99,18 +117,34 @@ export const managedAgentProjectBuild = defineCommand({
"zh-CN": "整理目录源文件并生成不可变的发布 Build",
},
auth: "none",
risk: {
level: "high",
message: {
notes: [
{
"en-US":
"This organizes project source, moves literal Vault secrets into the local .env, and writes the previewed immutable Build.",
"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.",
"zh-CN":
"该操作会整理项目文件,将 Vault 明文密钥移入本地 .env,并写入已预览的不可变 Build。",
"Build 无需确认即可写入本地项目文件,包括推断的资源关联及将 Vault 明文密钥移入 .env。使用 --dry-run 可只预览不写入。Publish 变更远端资源前仍需显式确认。",
},
},
],
usageArgs: "[--project <directory>]",
flags: PROJECT_FLAG,
exampleArgs: ["--dry-run", "--yes", "--project ./my-agent --yes"],
exampleArgs: ["", "--dry-run", "--project ./my-agent"],
async validate(flags) {
await withAgentErrors(async () => {
const root = await resolveDirectoryProjectRoot(flags.project ?? ".");
const metadata = await stat(join(root, "project.json")).catch((error: unknown) => {
if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
throw error;
});
if (!metadata?.isFile()) {
throw new BailianError(
`Not a project root: ${root} (project.json is missing).`,
ExitCode.USAGE,
"Run from the directory containing project.json, or pass --project <directory>.",
);
}
});
return undefined;
},
async run(ctx) {
const root = ctx.flags.project ?? ".";
const preview = await previewProjectBuild(root);
@@ -120,7 +154,11 @@ export const managedAgentProjectBuild = defineCommand({
return;
}
if (!preview.can_build)
throw new BailianError("Directory project is invalid and cannot be built.", ExitCode.GENERAL);
throw new BailianError(
preview.diagnostics.find((diagnostic) => diagnostic.severity === "error")?.message ??
"Directory project is invalid and cannot be built.",
ExitCode.GENERAL,
);
const built = await commitProjectBuild({
projectRoot: root,
baseRevision: preview.project_revision,
@@ -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");
},
);
});
@@ -0,0 +1,247 @@
import {
cp,
mkdir,
mkdtemp,
readFile,
realpath,
rm,
stat,
symlink,
writeFile,
} from "node:fs/promises";
import { tmpdir } from "node:os";
import { 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, runCommandHelp } from "./helpers.ts";
const routes = {
"managed-agent project init": "managedAgentProjectInit",
"managed-agent project build": "managedAgentProjectBuild",
"managed-agent project publish": "managedAgentProjectPublish",
"managed-agent project validate": "managedAgentProjectValidate",
} as const;
const directories: string[] = [];
async function temporaryDirectory() {
const directory = await realpath(await mkdtemp(join(tmpdir(), "bailian-project-init-")));
directories.push(directory);
return directory;
}
afterEach(async () => {
for (const directory of directories.splice(0)) {
await rm(directory, { recursive: true, force: true });
}
});
function runInit(directory: string, args: string[] = []) {
return runProject(directory, "init", args);
}
async function runProject(directory: string, subcommand: string, args: string[] = [], json = true) {
const configRoot = await temporaryDirectory();
return runNodeMain(
fileURLToPath(new URL("./harness/main.ts", import.meta.url)),
["managed-agent", "project", subcommand, ...args, ...(json ? ["--output", "json"] : [])],
{
cwd: directory,
env: {
BAILIAN_CONFIG_DIR: configRoot,
BAILIAN_E2E_ROUTES: JSON.stringify(
Object.entries(routes).map(([path, exportName]) => ({ path, export: exportName })),
),
},
},
);
}
describe("e2e: managed-agent project init directory defaults", () => {
test("build links copied resources and reports ambiguous environment bindings without writing", async () => {
const directory = await temporaryDirectory();
const initialized = await runInit(directory);
expect(initialized.exitCode, initialized.stderr).toBe(0);
const root = join(directory, "managed-agent");
for (const [section, id] of [
["skills", "example-skill"],
["files", "example-file"],
["environments", "example-env"],
["vaults", "example-vault"],
] as const) {
await cp(
join(root, "agents/assistant", section, "_examples", id),
join(root, "agents/assistant", section, id),
{ recursive: true },
);
}
const agentPath = join(root, "agents/assistant/agent.json");
const original = await readFile(agentPath, "utf8");
const preview = await runProject(root, "build", ["--dry-run"]);
expect(preview.exitCode, preview.stderr).toBe(0);
expect(parseStdoutJson<{ can_build: boolean }>(preview.stdout).can_build).toBe(true);
expect(await readFile(agentPath, "utf8")).toBe(original);
const built = await runProject(root, "build");
expect(built.exitCode, built.stderr).toBe(0);
const agent = JSON.parse(await readFile(agentPath, "utf8"));
expect(agent).toMatchObject({
environment: "example-env",
vault: "example-vault",
skills: ["example-skill"],
files: [{ file: "example-file", mount_path: "/mnt/example.md" }],
});
const alternatePath = join(root, "agents/assistant/environments/alternate");
await mkdir(alternatePath);
await writeFile(
join(alternatePath, "environment.json"),
JSON.stringify({ id: "alternate", config: { type: "cloud" } }),
);
delete agent.environment;
await writeFile(agentPath, JSON.stringify(agent));
const beforeConflict = await readFile(agentPath, "utf8");
const buildPath = join(root, ".openagentpack/build/agents.yaml");
const beforeBuild = await readFile(buildPath, "utf8");
for (const json of [false, true]) {
const conflict = await runProject(root, "build", [], json);
expect(conflict.exitCode).toBe(1);
expect(conflict.stderr).toContain("multiple local environment resources");
expect(conflict.stderr).toContain("Set 'environment' explicitly");
expect(conflict.stderr).not.toMatch(/\p{Script=Han}/u);
}
expect(await readFile(agentPath, "utf8")).toBe(beforeConflict);
expect(await readFile(buildPath, "utf8")).toBe(beforeBuild);
});
test("build checks directories and writes without confirmation while publish stays gated", async () => {
const directory = await temporaryDirectory();
const initialized = await runInit(directory);
expect(initialized.exitCode, initialized.stderr).toBe(0);
const root = join(directory, "managed-agent");
const nested = join(root, "agents/assistant/skills");
for (const args of [[], ["--dry-run"]]) {
const result = await runProject(nested, "build", args, false);
expect(result.exitCode, result.stderr).toBe(2);
expect(result.stderr).toContain("Not a project root:");
expect(result.stderr).not.toMatch(/\p{Script=Han}/u);
expect(result.stderr).toContain(`cd '${root}'`);
expect(result.stderr).not.toContain("high-risk");
expect(result.stderr).not.toContain("Usage:");
}
const explicit = await runProject(root, "build", ["--project", nested]);
expect(explicit.exitCode).toBe(2);
expect(explicit.stderr).toContain(`--project '${root}'`);
const noMarker = await runProject(directory, "build", [], false);
expect(noMarker.exitCode).toBe(2);
expect(noMarker.stderr).toContain("project.json");
expect(noMarker.stderr).not.toMatch(/\p{Script=Han}/u);
expect(noMarker.stderr).not.toContain("high-risk");
expect(await stat(join(nested, ".openagentpack")).catch(() => null)).toBeNull();
const preview = await runProject(root, "build", ["--dry-run"]);
expect(preview.exitCode, preview.stderr).toBe(0);
expect(await stat(join(root, ".openagentpack/build")).catch(() => null)).toBeNull();
const built = await runProject(root, "build");
expect(built.exitCode, built.stderr).toBe(0);
expect(built.stderr).not.toContain("requires_confirmation");
expect((await stat(join(root, ".openagentpack/build/agents.yaml"))).isFile()).toBe(true);
const explicitValid = await runProject(nested, "build", ["--project", root]);
expect(explicitValid.exitCode, explicitValid.stderr).toBe(0);
const storePath = join(root, ".openagentpack/versions/project/store.json");
const beforePublish = await readFile(storePath, "utf8");
const publish = await runProject(root, "publish");
expect(publish.exitCode).toBe(7);
expect(publish.stderr).toContain("requires_confirmation");
expect(await readFile(storePath, "utf8")).toBe(beforePublish);
});
test("only publish help includes confirmation; build keeps dry-run", async () => {
const build = await runCommandHelp(routes, ["managed-agent", "project", "build", "--help"]);
expect(build.stderr).not.toContain("--yes");
expect(build.stderr).toContain("--dry-run");
const publish = await runCommandHelp(routes, ["managed-agent", "project", "publish", "--help"]);
expect(publish.stderr).toContain("--yes");
const directory = await temporaryDirectory();
const removedFlag = await runProject(directory, "build", ["--yes"]);
expect(removedFlag.exitCode).toBe(2);
expect(removedFlag.stderr).toMatch(/Unknown flag.*--yes/);
});
test("nested build and validate explain the project root without changing directories", async () => {
const directory = await temporaryDirectory();
const initialized = await runInit(directory);
expect(initialized.exitCode, initialized.stderr).toBe(0);
const root = join(directory, "managed-agent");
const nested = join(root, "agents/assistant/skills");
for (const command of ["build", "validate"]) {
const result = await runProject(nested, command, ["--dry-run"]);
expect(result.exitCode).not.toBe(0);
expect(result.stderr).toContain("Not a project root:");
expect(result.stderr).not.toMatch(/\p{Script=Han}/u);
expect(result.stderr).toContain(`cd '${root}'`);
expect(result.stderr).toContain(`--project '${root}'`);
expect(result.stderr).not.toContain("ERR_MODULE_NOT_FOUND");
}
expect(await stat(join(nested, ".openagentpack")).catch(() => null)).toBeNull();
const corrected = await runProject(nested, "build", ["--project", root, "--dry-run"]);
expect(corrected.exitCode, corrected.stderr).toBe(0);
});
test("help describes the subdirectory default and explicit in-place initialization", async () => {
const result = await runCommandHelp(routes, ["managed-agent", "project", "init", "--help"]);
expect(result.exitCode).toBe(0);
expect(result.stderr).toContain("./managed-agent");
expect(result.stderr).toContain("--project .");
});
test("default dry-run reports the child directory without creating it", async () => {
const directory = await temporaryDirectory();
const result = await runInit(directory, ["--dry-run"]);
expect(result.exitCode, result.stderr).toBe(0);
expect(parseStdoutJson(result.stdout)).toEqual({ would_initialize_project: "./managed-agent" });
expect(await stat(join(directory, "managed-agent")).catch(() => null)).toBeNull();
});
test("initializes only the child, ignores parent source, and rejects repeated init without overwriting", async () => {
const directory = await temporaryDirectory();
const parentYaml = "not a valid project declaration";
await writeFile(join(directory, "agents.yaml"), parentYaml);
if (process.platform !== "win32") {
await symlink("missing-instructions.md", join(directory, "CLAUDE.md"));
}
const result = await runInit(directory);
expect(result.exitCode, result.stderr).toBe(0);
const projectRoot = join(directory, "managed-agent");
const initialized = parseStdoutJson<{
project_root: string;
baseline_version: string;
converted_from_yaml: boolean;
}>(result.stdout);
expect(initialized.project_root).toBe(projectRoot);
expect(initialized.baseline_version).toHaveLength(64);
expect(initialized.converted_from_yaml).toBe(false);
expect(await stat(join(directory, "project.json")).catch(() => null)).toBeNull();
expect(await readFile(join(directory, "agents.yaml"), "utf8")).toBe(parentYaml);
expect((await stat(join(projectRoot, "agents/assistant/agent.json"))).isFile()).toBe(true);
expect(
(await stat(join(projectRoot, ".openagentpack/versions/project/store.json"))).isFile(),
).toBe(true);
const instructions = join(projectRoot, "agents/assistant/instructions.md");
await writeFile(instructions, "user changes");
const repeated = await runInit(directory);
expect(repeated.exitCode).not.toBe(0);
expect(repeated.stderr).toContain("already exists");
expect(await readFile(instructions, "utf8")).toBe("user changes");
});
test("explicit paths, including dot, remain exact project roots", async () => {
for (const target of ["custom-agent", "."]) {
const directory = await temporaryDirectory();
const result = await runInit(directory, ["--project", target]);
expect(result.exitCode, result.stderr).toBe(0);
expect(parseStdoutJson<{ project_root: string }>(result.stdout).project_root).toBe(
join(directory, target),
);
expect(await stat(join(directory, "managed-agent")).catch(() => null)).toBeNull();
}
});
});
@@ -368,7 +368,6 @@ describe("e2e: managed-agent", () => {
"build",
"--project",
projectRoot,
"--yes",
"--output",
"json",
]);
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "bailian-cli-core",
"version": "1.21.0",
"version": "1.22.0",
"description": "Core SDK for bailian-cli. See https://www.npmjs.com/package/bailian-cli for usage.",
"homepage": "https://bailian.console.aliyun.com/cli",
"bugs": {
+3 -1
View File
@@ -287,8 +287,10 @@ export interface Command<F extends FlagsDef = FlagsDef> {
* Cross-flag validation, after parsing and before run. Return an error message
* → UsageError; undefined to pass. Single-flag `required` is enforced by the
* parser — use this for rules spanning flags or depending on a flag's *value*.
* May be async for read-only local preflight. Runtime awaits it before auth
* and confirmation. Do not perform remote requests or local writes here.
*/
validate?: (flags: ParsedFlags<F>) => string | undefined;
validate?: (flags: ParsedFlags<F>) => string | undefined | Promise<string | undefined>;
run: (ctx: CommandContext<F>) => Promise<void>;
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "knowledge-studio-cli",
"version": "1.21.0",
"version": "1.22.0",
"description": "Lightweight RAG CLI for Aliyun Model Studio — focused on knowledge-base retrieval.",
"keywords": [
"alibaba-cloud",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "bailian-cli-runtime",
"version": "1.21.0",
"version": "1.22.0",
"description": "Runtime framework for bailian-cli (createCli, registry, args, output, pipeline). See https://www.npmjs.com/package/bailian-cli for usage.",
"homepage": "https://bailian.console.aliyun.com/cli",
"bugs": {
+1 -1
View File
@@ -217,7 +217,7 @@ export function createCli(commands: Record<string, AnyCommand>, opts: CliOptions
parsedFlags,
Object.keys(res.command.flags ?? {}),
) as ParsedFlags<FlagsDef>;
const invalid = res.command.validate?.(ownFlags);
const invalid = await res.command.validate?.(ownFlags);
if (invalid) throw new UsageError(invalid);
// 校验通过 → 建源、解析 settings、组 ctx,进中间件执行命令。
+5 -5
View File
@@ -189,8 +189,8 @@ importers:
packages/commands:
dependencies:
'@openagentpack/sdk':
specifier: 0.7.0
version: 0.7.0
specifier: 0.7.1
version: 0.7.1
bailian-cli-core:
specifier: workspace:*
version: link:../core
@@ -1124,8 +1124,8 @@ packages:
'@emnapi/core': ^1.7.1
'@emnapi/runtime': ^1.7.1
'@openagentpack/sdk@0.7.0':
resolution: {integrity: sha512-MjyKIFvPi4aT1TIcvGUB0bnD4Tly9aMCncjI9+EYG6CPWpuGO4DGj+4zlfBCx1fvtD1G1w4Zi0m8A8ZodqkK9Q==}
'@openagentpack/sdk@0.7.1':
resolution: {integrity: sha512-OENks8UdfWFanvhJIVbqyUX1+XGavHnrxDiFx4SoJoGRXi45cOgyYtWWbG5VdDI2I5Dpo9eGNaw/JtDnkdxUcg==}
engines: {node: '>=18.17.0'}
'@oxc-project/runtime@0.129.0':
@@ -4287,7 +4287,7 @@ snapshots:
'@tybys/wasm-util': 0.10.1
optional: true
'@openagentpack/sdk@0.7.0':
'@openagentpack/sdk@0.7.1':
dependencies:
jszip: 3.10.1
yaml: 2.9.0
+1 -1
View File
@@ -1,7 +1,7 @@
---
name: bailian-cli
metadata:
version: "1.21.0"
version: "1.22.0"
requires:
bins: ["bl"]
description: >-
+1 -1
View File
@@ -1,7 +1,7 @@
---
name: bailian-finetune
metadata:
version: "1.21.0"
version: "1.22.0"
requires:
bins: ["bl"]
description: >-
+1 -1
View File
@@ -1,7 +1,7 @@
---
name: bailian-gen
metadata:
version: "1.21.0"
version: "1.22.0"
requires:
bins: ["bl"]
description: >-
+1 -1
View File
@@ -1,7 +1,7 @@
---
name: bailian-managed-agent
metadata:
version: "1.21.0"
version: "1.22.0"
requires:
bins: ["bl"]
description: >-
@@ -1181,38 +1181,35 @@ bl managed-agent playground --file agents.yaml --no-open
### `bl managed-agent project build`
| Field | Value |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------- |
| **Name** | `managed-agent project build` |
| **Description** | Organize directory source and generate the immutable Publish Build |
| **Authentication** | No Auth |
| **Usage** | `bl managed-agent project build [--project <directory>]` |
| **Risk** | `high` |
| **Risk message** | This organizes project source, moves literal Vault secrets into the local .env, and writes the previewed immutable Build. |
> **Agent safety:** Never add `--yes` automatically. On `type="requires_confirmation"`, stop and ask for explicit user confirmation of the same action and scope.
| Field | Value |
| ------------------ | ------------------------------------------------------------------ |
| **Name** | `managed-agent project build` |
| **Description** | Organize directory source and generate the immutable Publish Build |
| **Authentication** | No Auth |
| **Usage** | `bl managed-agent project build [--project <directory>]` |
#### Flags
| Flag | Type | Required | Description |
| ----------------------- | ------ | -------- | --------------------------------------------------- |
| `--project <directory>` | string | no | Directory project root (default: current directory) |
| `--yes` | switch | no | Confirm this high-risk operation |
#### Notes
- 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.
#### Examples
```bash
bl managed-agent project build
```
```bash
bl managed-agent project build --dry-run
```
```bash
# Only after explicit user confirmation:
bl managed-agent project build --yes
```
```bash
# Only after explicit user confirmation:
bl managed-agent project build --project ./my-agent --yes
bl managed-agent project build --project ./my-agent
```
### `bl managed-agent project init`
@@ -1226,12 +1223,13 @@ bl managed-agent project build --project ./my-agent --yes
#### Flags
| Flag | Type | Required | Description |
| ----------------------- | ------ | -------- | --------------------------------------------------- |
| `--project <directory>` | string | no | Directory project root (default: current directory) |
| Flag | Type | Required | Description |
| ----------------------- | ------ | -------- | ----------------------------------------------------------------------------- |
| `--project <directory>` | string | no | Directory project root (default: ./managed-agent under the current directory) |
#### Notes
- 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.
- 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.
#### Examples
@@ -1244,6 +1242,10 @@ bl managed-agent project init
bl managed-agent project init --project ./my-agent
```
```bash
bl managed-agent project init --project .
```
### `bl managed-agent project publish`
| Field | Value |
+1 -1
View File
@@ -1,7 +1,7 @@
---
name: bailian-protocol
metadata:
version: "1.21.0"
version: "1.22.0"
requires:
bins: ["bl"]
description: >-
+1 -1
View File
@@ -1,7 +1,7 @@
---
name: bailian-web-search
metadata:
version: "1.21.0"
version: "1.22.0"
requires:
bins: ["bl"]
description: >-