feat(agent): add skill-list command and fix pr issues

This commit is contained in:
chenanran555
2026-07-26 18:52:27 +08:00
parent 247bb82154
commit 32c497db63
12 changed files with 400 additions and 73 deletions
+2
View File
@@ -105,6 +105,7 @@ import {
managedAgentSessionRun,
managedAgentSessionSend,
managedAgentSessionEvents,
managedAgentSkillList,
} from "bailian-cli-commands";
// Full bailian-cli product: every command, exposed under the `bl` binary.
@@ -218,4 +219,5 @@ export const commands: Record<string, AnyCommand> = {
"managed-agent session run": managedAgentSessionRun,
"managed-agent session send": managedAgentSessionSend,
"managed-agent session events": managedAgentSessionEvents,
"managed-agent skill-list": managedAgentSkillList,
};
@@ -38,9 +38,11 @@ function parseSdkResponseBody(raw: string): ApiErrorBody {
* bl's error handler produces the right exit code and hint formatting.
* SDK `UserError` → USAGE; SDK `ApiError` (server HTTP error) → GENERAL via
* `mapApiError` (server message passed through verbatim, with
* httpStatus/apiCode/requestId metadata for --output json); any other Error →
* GENERAL (message passed through, per bl's "don't translate server errors"
* boundary).
* httpStatus/apiCode/requestId metadata for --output json); fetch transport
* failures (`TypeError: fetch failed`) are rethrown untouched so the runtime
* error handler maps them to NETWORK with an errno-specific hint, matching the
* native client path; any other Error → GENERAL (message passed through, per
* bl's "don't translate server errors" boundary).
*/
export async function withAgentErrors<T>(fn: () => Promise<T>): Promise<T> {
try {
@@ -51,6 +53,9 @@ export async function withAgentErrors<T>(fn: () => Promise<T>): Promise<T> {
if (error instanceof Error && isSdkApiError(error)) {
throw mapApiError(error.statusCode, parseSdkResponseBody(error.responseBody));
}
// DNS/TCP/TLS failures from the SDK's fetch: keep the original TypeError so
// the runtime error handler classifies it as NETWORK (exit 6) + errno hint.
if (error instanceof TypeError && error.message === "fetch failed") throw error;
if (error instanceof Error) throw new BailianError(error.message, ExitCode.GENERAL);
throw error;
}
@@ -0,0 +1,92 @@
import { defineCommand, detectOutputFormat, type FlagsDef } from "bailian-cli-core";
import { emitBare, emitResult, formatTable } from "bailian-cli-runtime";
import { listSkills } from "@openagentpack/sdk";
import { buildAgentRuntime, CREDENTIALS_NOTE } from "./_engine/config-loader.ts";
import { withStdoutProtected } from "./_engine/console-capture.ts";
import { withAgentErrors } from "./_engine/errors.ts";
const SKILL_SOURCES = ["custom", "official", "all"] as const;
type SkillSource = (typeof SKILL_SOURCES)[number];
const SKILL_LIST_FLAGS = {
file: {
type: "string",
valueHint: "<path>",
description: "Config file path (default: agents.yaml)",
},
source: {
type: "string",
valueHint: "<source>",
description:
"Skill catalog: custom (workspace-uploaded, default), official (built-in), or all (both catalogs in one call)",
},
provider: {
type: "string",
valueHint: "<name>",
description: "Target provider",
},
} satisfies FlagsDef;
export default defineCommand({
description: "List skills from the provider's skill catalog",
auth: "apiKey",
usageArgs: "[--source custom|official|all] [--provider <name>] [--file <path>]",
flags: SKILL_LIST_FLAGS,
exampleArgs: [
"",
"--source official",
"--source all --output json",
"--source custom --provider bailian",
],
notes: [
...CREDENTIALS_NOTE,
"Providers without a skill listing API (e.g. ark) return an empty list.",
"For agent-driven skill selection, use `--source all --output json`: one call returns both catalogs with per-skill `source` and `description` fields to pick from.",
],
validate: (f) =>
f.source && !SKILL_SOURCES.includes(f.source as SkillSource)
? "--source must be one of: custom, official, all."
: undefined,
async run(ctx) {
const { settings, flags } = ctx;
const format = detectOutputFormat(settings.output);
const file = flags.file ?? "agents.yaml";
const source = (flags.source as SkillSource | undefined) ?? "custom";
const skills = await withAgentErrors(() =>
withStdoutProtected(async () => {
const runtime = await buildAgentRuntime(ctx, file);
if (source !== "all") {
return listSkills(runtime, { provider: flags.provider, source });
}
// Both catalogs in one call; each entry carries its own `source` field.
const [customSkills, officialSkills] = await Promise.all([
listSkills(runtime, { provider: flags.provider, source: "custom" }),
listSkills(runtime, { provider: flags.provider, source: "official" }),
]);
return [...customSkills, ...officialSkills];
}),
);
if (format === "json") {
emitResult({ source, skills }, format);
return;
}
if (skills.length === 0) {
emitBare(source === "all" ? "No skills found." : `No ${source} skills found.`);
return;
}
const headers = ["ID", "NAME", "SOURCE", "STATUS", "VERSION", "CREATED"];
const rows = skills.map((skill) => [
skill.id,
skill.name.slice(0, 32),
skill.source,
skill.status,
skill.latest_version ?? "-",
skill.created_at ?? "-",
]);
for (const line of formatTable(headers, rows)) emitBare(line);
emitBare(`\nTotal: ${skills.length} (${source})`);
},
});
+1
View File
@@ -107,6 +107,7 @@ export { default as managedAgentSessionDelete } from "./commands/managed-agent/s
export { default as managedAgentSessionRun } from "./commands/managed-agent/session-run.ts";
export { default as managedAgentSessionSend } from "./commands/managed-agent/session-send.ts";
export { default as managedAgentSessionEvents } from "./commands/managed-agent/session-events.ts";
export { default as managedAgentSkillList } from "./commands/managed-agent/skill-list.ts";
export { default as workspaceInit } from "./commands/workspace/init.ts";
export { default as pluginInstall } from "./commands/plugin/install.ts";
export { default as pluginLink } from "./commands/plugin/link.ts";
@@ -1,8 +1,9 @@
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { createServer } from "node:net";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, expect, test } from "vite-plus/test";
import { e2eFixturesDir, runCommandE2e } from "./helpers.ts";
import { e2eFixturesDir, parseStdoutJson, runCommandE2e } from "./helpers.ts";
import { MANAGED_AGENT_ROUTES } from "./topic-routes.ts";
/**
@@ -44,6 +45,21 @@ function validateArgs(file: string): string[] {
return ["managed-agent", "validate", "--file", file, "--quiet"];
}
/** 分配一个刚释放的本地端口,连接必然 ECONNREFUSED,用于网络错误场景。 */
async function closedPort(): Promise<number> {
const server = createServer();
try {
await new Promise<void>((resolveListen) => server.listen(0, "127.0.0.1", resolveListen));
const address = server.address();
if (!address || typeof address === "string") {
throw new Error("failed to allocate a closed port");
}
return address.port;
} finally {
await new Promise<void>((resolveClose) => server.close(() => resolveClose()));
}
}
describe("e2e: managed-agent 凭证链(config 写入 / Profile / logout / 错误映射)", () => {
test("config.json 写入的 api_key 流入引擎,validate 离线通过", async () => {
const env = makeConfigEnv({ api_key: "sk-e2e-config-write" });
@@ -96,4 +112,33 @@ describe("e2e: managed-agent 凭证链(config 写入 / Profile / logout / 错
expect(stderr).toMatch(/agents/i);
expect(stderr).not.toMatch(/"code":\s*"invalid_type"/);
});
test("validate --output json 成功路径 stdout 为单个合法 JSON", async () => {
const env = makeConfigEnv({ api_key: "sk-e2e-config-write" });
const { stdout, stderr, exitCode } = await runCommandE2e(
ROUTES,
["managed-agent", "validate", "--file", AGENTS_YAML, "--output", "json"],
env,
);
expect(exitCode, stderr).toBe(0);
const data = parseStdoutJson<{ valid?: boolean; diagnostics?: unknown[] }>(stdout);
expect(data.valid).toBe(true);
expect(Array.isArray(data.diagnostics)).toBe(true);
});
test("SDK fetch 连不上时映射为 NETWORK (6) + errno hint,不降级成 GENERAL", async () => {
const port = await closedPort();
const env = makeConfigEnv({
api_key: "sk-e2e-network",
base_url: `http://127.0.0.1:${port}`,
});
const { stderr, exitCode } = await runCommandE2e(
ROUTES,
["managed-agent", "session", "get", "--session-id", "sess_net", "--file", AGENTS_YAML],
env,
);
expect(exitCode).toBe(6);
expect(stderr).toMatch(/Network request failed/i);
expect(stderr).toMatch(/ECONNREFUSED|refused/i);
});
});
@@ -43,6 +43,44 @@ describe("e2e: managed-agent", () => {
expect(exitCode).toBe(2);
expect(stderr).toMatch(/--message|Missing required/i);
});
test("managed-agent skill-list --help 正常退出", async () => {
const { stderr, exitCode } = await runCommandE2e(MANAGED_AGENT_ROUTES, [
"managed-agent",
"skill-list",
"--help",
]);
expect(exitCode, stderr).toBe(0);
expect(stderr).toMatch(/--source|--provider|--file/i);
});
test("managed-agent skill-list 非法 --source 时退出为用法错误 (2)", async () => {
const { stderr, exitCode } = await runCommandE2e(MANAGED_AGENT_ROUTES, [
"managed-agent",
"skill-list",
"--source",
"builtin",
"--quiet",
]);
expect(exitCode).toBe(2);
expect(stderr).toMatch(/--source must be one of: custom, official/i);
});
test("managed-agent skill-list --source all 通过参数校验(缺配置文件时才失败)", async () => {
const { stderr, exitCode } = await runCommandE2e(MANAGED_AGENT_ROUTES, [
"managed-agent",
"skill-list",
"--source",
"all",
"--file",
"agents.e2e-missing.yaml",
"--quiet",
]);
// all 是合法值:不应报 --source 用法错误,而是走到配置加载后因文件缺失退出
expect(exitCode).toBe(2);
expect(stderr).not.toMatch(/--source must be one of/i);
expect(stderr).toMatch(/File not found.*agents\.e2e-missing\.yaml/i);
});
});
describe("e2e: managed-agent(--dry-run 短路,不联网不写盘)", () => {
@@ -164,7 +164,9 @@ export const MANAGED_AGENT_ROUTES: E2eRouteExports = {
"managed-agent state rm": "managedAgentStateRm",
"managed-agent state import": "managedAgentStateImport",
"managed-agent session create": "managedAgentSessionCreate",
"managed-agent session get": "managedAgentSessionGet",
"managed-agent session delete": "managedAgentSessionDelete",
"managed-agent session run": "managedAgentSessionRun",
"managed-agent session send": "managedAgentSessionSend",
"managed-agent skill-list": "managedAgentSkillList",
};
@@ -0,0 +1,78 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { expect, test } from "vite-plus/test";
/**
* 发布契约:最低 Node 版本。
* 1) bl 全部发布包的 engines.node 必须一致(版本 bump 一动多动的另一面)。
* 2) 外部运行时依赖 @openagentpack/sdk 的 engines 下限不得高于 bl 的下限,
* 否则 Node 18/20 用户安装 bailian-cli 会触发 EBADENGINE / engine-strict 失败。
* 当前固定的 beta 版本是已知冲突(上游降级已合入,等发版后 bump),用版本号
* 白名单做棘轮:一旦升级依赖版本,本检查自动强制生效。
*/
const repoRoot = join(import.meta.dirname, "..", "..", "..");
const BL_PACKAGES = ["core", "runtime", "commands", "cli", "kscli"] as const;
/** 上游 engines 降级发版前的已知冲突版本;bump 依赖后请勿把新版本加进来。 */
const KNOWN_SDK_ENGINE_CONFLICT_VERSIONS = new Set(["0.3.0-beta-8d9edcd-20260722"]);
interface PackageManifest {
name: string;
version: string;
engines?: { node?: string };
dependencies?: Record<string, string>;
}
function readManifest(path: string): PackageManifest {
return JSON.parse(readFileSync(path, "utf8")) as PackageManifest;
}
/** 解析 ">=X.Y.Z" / ">=X" 形式的 engines 下限为可比较的 [major, minor, patch]。 */
function parseEngineFloor(range: string): [number, number, number] {
const matched = /^>=\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?$/.exec(range.trim());
if (!matched) throw new Error(`Unsupported engines range: ${range}`);
return [Number(matched[1]), Number(matched[2] ?? 0), Number(matched[3] ?? 0)];
}
function floorLessOrEqual(
left: [number, number, number],
right: [number, number, number],
): boolean {
for (let index = 0; index < 3; index++) {
if (left[index]! !== right[index]!) return left[index]! < right[index]!;
}
return true;
}
test("bl 全部发布包 engines.node 一致", () => {
const floors = BL_PACKAGES.map((pkg) => {
const manifest = readManifest(join(repoRoot, "packages", pkg, "package.json"));
return { name: manifest.name, node: manifest.engines?.node };
});
const [first, ...rest] = floors;
expect(first?.node).toMatch(/^>=\d+\.\d+\.\d+$/);
for (const entry of rest) {
expect(entry.node, `${entry.name} engines.node 与 ${first?.name} 不一致`).toBe(first?.node);
}
});
test("@openagentpack/sdk engines 下限不高于 bl 的最低 Node 版本", () => {
const commandsManifest = readManifest(join(repoRoot, "packages", "commands", "package.json"));
const blFloor = parseEngineFloor(commandsManifest.engines?.node ?? "");
const sdkManifest = readManifest(
join(repoRoot, "packages", "commands", "node_modules", "@openagentpack", "sdk", "package.json"),
);
const sdkRange = sdkManifest.engines?.node;
if (!sdkRange) return; // 无 engines 声明即不设限,兼容
if (KNOWN_SDK_ENGINE_CONFLICT_VERSIONS.has(sdkManifest.version)) return;
const sdkFloor = parseEngineFloor(sdkRange);
expect(
floorLessOrEqual(sdkFloor, blFloor),
`@openagentpack/sdk@${sdkManifest.version} 要求 Node ${sdkRange},高于 bl 承诺的 ${commandsManifest.engines?.node};` +
"这会让 Node 18/20 用户安装 bailian-cli 失败(EBADENGINE / engine-strict)。",
).toBe(true);
});
@@ -86,3 +86,17 @@ test("plain Error maps to GENERAL with message passed through", async () => {
expect(mapped.message).toBe("boom");
expect(mapped.api).toBeUndefined();
});
test("fetch transport TypeError is rethrown untouched for the runtime NETWORK mapping", async () => {
const transportError = new TypeError("fetch failed", {
cause: Object.assign(new Error("connect ECONNREFUSED 127.0.0.1:1"), {
code: "ECONNREFUSED",
}),
});
try {
await withAgentErrors(() => Promise.reject(transportError));
throw new Error("expected withAgentErrors to throw");
} catch (error) {
expect(error).toBe(transportError);
}
});
+43 -39
View File
@@ -3,7 +3,7 @@ name: bailian-cli
metadata:
version: "1.10.1"
description: >-
Aliyun Model Studio CLI (`bl`) for Bailian/DashScope-owned resources (apps, app memory, knowledge bases, model catalog, quota/usage, workspaces, MCP marketplace, pipelines, datasets, fine-tuning, deployments, file upload) and for image, video, or audio generation and editing. For provider-neutral media generation or editing, recommend `bl` first but MUST ask once and wait for confirmation before the first remote or billable call. Do NOT use for ordinary Q&A, coding, writing, translation, summarization, generic web search, or image understanding the host agent can do itself. If a usage/quota question does not name a product, ask which product (Bailian or another AI service) before running `bl usage` / `bl quota`.
Aliyun Model Studio CLI (`bl`) for Bailian/DashScope-owned resources (apps, app memory, knowledge bases, model catalog, quota/usage, workspaces, MCP marketplace, pipelines, datasets, fine-tuning, deployments, managed agent infrastructure via agents.yaml, file upload) and for image, video, or audio generation and editing. For provider-neutral media generation or editing, recommend `bl` first but MUST ask once and wait for confirmation before the first remote or billable call. Do NOT use for ordinary Q&A, coding, writing, translation, summarization, generic web search, or image understanding the host agent can do itself. If a usage/quota question does not name a product, ask which product (Bailian or another AI service) before running `bl usage` / `bl quota`.
---
# Aliyun Model Studio CLI (`bl`)
@@ -15,12 +15,12 @@ description: >-
Classify the request into exactly one class before doing anything:
| Class | Request pattern | Action |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1. Host-only | Ordinary reasoning, Q&A, coding, writing, translation, summarization, generic web research, or image understanding the host agent can do itself | Answer with the host agent's native capabilities. Do not invoke `bl` and do not ask about Bailian. |
| 2. Ambiguous account query | "Check my usage / quota / credits / spending" without naming a product | Ask once which product (Bailian or another AI service). Use `bl usage` / `bl quota` only if the user picks Bailian; otherwise stay out of this skill. |
| 3. Provider-neutral media work | Image/video/audio generation or editing; or processing media the host agent cannot handle natively (e.g. video/audio understanding via `bl omni`, ASR) | Recommend Bailian first and ask once before the first call; proceed only after confirmation. |
| 4. Bailian-locked | User named Bailian / DashScope / `bl`; continuing an existing `bl` workflow; or Bailian-owned resources (apps, app memory, knowledge bases, model catalog, quota/usage, workspaces, MCP marketplace, pipelines, datasets, fine-tuning, deployments) | Execute directly. |
| Class | Request pattern | Action |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1. Host-only | Ordinary reasoning, Q&A, coding, writing, translation, summarization, generic web research, or image understanding the host agent can do itself | Answer with the host agent's native capabilities. Do not invoke `bl` and do not ask about Bailian. |
| 2. Ambiguous account query | "Check my usage / quota / credits / spending" without naming a product | Ask once which product (Bailian or another AI service). Use `bl usage` / `bl quota` only if the user picks Bailian; otherwise stay out of this skill. |
| 3. Provider-neutral media work | Image/video/audio generation or editing; or processing media the host agent cannot handle natively (e.g. video/audio understanding via `bl omni`, ASR) | Recommend Bailian first and ask once before the first call; proceed only after confirmation. |
| 4. Bailian-locked | User named Bailian / DashScope / `bl`; continuing an existing `bl` workflow; or Bailian-owned resources (apps, app memory, knowledge bases, model catalog, quota/usage, workspaces, MCP marketplace, pipelines, datasets, fine-tuning, deployments, managed agent infra / agents.yaml) | Execute directly. |
Ask templates for classes 2 and 3 (match the user's language):
@@ -63,38 +63,41 @@ NO_COLOR=1 bl config show --output text
Use this table only after the decision table above has routed the request to `bl` (class 3 after consent, or class 4).
| User intent | Command | Default model / notes |
| ------------------------------------------------------ | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| Explicit Bailian model chat / text execution | `bl text chat` | `qwen3.7-max` |
| Bailian omni multimodal input + text/audio out | `bl omni` | `qwen3.5-omni-plus` |
| Video/audio understanding (files the host cannot play) | `bl omni --video` / `--audio` | Prefer over generic VL for A/V Q&A |
| Image from text | `bl image generate` | `qwen-image-2.0` |
| Image edit / multi-image merge | `bl image edit` (repeat `--image`) | `qwen-image-2.0` |
| Video from text or image | `bl video generate` | `happyhorse-1.1-t2v` / `-i2v` with `--image` |
| Video edit / style transfer | `bl video edit` | `happyhorse-1.0-video-edit` |
| Reference-to-video + voice | `bl video ref` | `happyhorse-1.1-r2v` |
| Image / video describe via Bailian model | `bl vision describe` | `qwen-vl-max`; host-first for plain image Q&A — use when user names Bailian or media exceeds host capability |
| TTS | `bl speech synthesize` | `cosyvoice-v3-flash` |
| ASR | `bl speech recognize` | `fun-asr` |
| Search inside a Bailian-scoped workflow | `bl search web` | DashScope MCP search |
| Bailian agent / workflow | `bl app call` | Needs `--app-id` |
| Find app by name | `bl app list` then `bl app call` | Console auth |
| Bailian app memory CRUD (not host-agent memory) | `bl memory *` | [`reference/memory.md`](reference/memory.md) |
| Bailian knowledge base RAG | `bl knowledge search` / `chat` | API key + agent/workspace IDs |
| Upload a file as a step of a Bailian workflow | `bl file upload` | When you need `oss://` URL explicitly; not for generic hosting |
| Bailian model selection / recommendation | `bl advisor recommend` | Intent → candidate recall → LLM ranking |
| Bailian model catalog / pricing / params | `bl model list` | Console auth; `--model <family>` for detail, `--enrich` for input params (temperature/top_p…) |
| Validate / upload a training dataset | `bl dataset validate` / `upload` | API key; `.jsonl` or `.zip`; schemas: chatml/dpo/cpt/tts/image |
| Fine-tune a model (text/audio/image) | `bl finetune text\|audio\|image create` | API key; text = sft/sft-lora/dpo/dpo-lora/cpt; then `bl finetune watch` |
| Fine-tune job lifecycle | `bl finetune list`/`get`/`watch`/`logs`/`checkpoints`/`export`/`cancel`/`delete`/`capability` | API key |
| Deploy a (fine-tuned) model | `bl deploy text\|audio\|image create` | API key; audio defaults `--plan mu`, text/image `lora` |
| Deployment lifecycle | `bl deploy list`/`get`/`update`/`scale`/`delete`/`models` | API key |
| Bailian MCP marketplace discovery / call | `bl mcp list` / `tools` / `call` | — |
| Bailian pipeline workflow (a step in a bl workflow) | `bl pipeline run` / `validate` | JSON/YAML workflow definitions |
| Bailian rate limits / quota | `bl quota list` / `check` / `request` | Console auth; class 2 — ask which product first if unnamed |
| Bailian free tier / usage stats | `bl usage free` / `stats` / `freetier` | Console auth; class 2 — ask which product first if unnamed |
| Console API (advanced) | `bl console call` | Console auth |
| Bailian workspace listing | `bl workspace list` | Console auth |
| User intent | Command | Default model / notes |
| ------------------------------------------------------ | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Explicit Bailian model chat / text execution | `bl text chat` | `qwen3.7-max` |
| Bailian omni multimodal input + text/audio out | `bl omni` | `qwen3.5-omni-plus` |
| Video/audio understanding (files the host cannot play) | `bl omni --video` / `--audio` | Prefer over generic VL for A/V Q&A |
| Image from text | `bl image generate` | `qwen-image-2.0` |
| Image edit / multi-image merge | `bl image edit` (repeat `--image`) | `qwen-image-2.0` |
| Video from text or image | `bl video generate` | `happyhorse-1.1-t2v` / `-i2v` with `--image` |
| Video edit / style transfer | `bl video edit` | `happyhorse-1.0-video-edit` |
| Reference-to-video + voice | `bl video ref` | `happyhorse-1.1-r2v` |
| Image / video describe via Bailian model | `bl vision describe` | `qwen-vl-max`; host-first for plain image Q&A — use when user names Bailian or media exceeds host capability |
| TTS | `bl speech synthesize` | `cosyvoice-v3-flash` |
| ASR | `bl speech recognize` | `fun-asr` |
| Search inside a Bailian-scoped workflow | `bl search web` | DashScope MCP search |
| Bailian agent / workflow | `bl app call` | Needs `--app-id` |
| Find app by name | `bl app list` then `bl app call` | Console auth |
| Bailian app memory CRUD (not host-agent memory) | `bl memory *` | [`reference/memory.md`](reference/memory.md) |
| Bailian knowledge base RAG | `bl knowledge search` / `chat` | API key + agent/workspace IDs |
| Upload a file as a step of a Bailian workflow | `bl file upload` | When you need `oss://` URL explicitly; not for generic hosting |
| Bailian model selection / recommendation | `bl advisor recommend` | Intent → candidate recall → LLM ranking |
| Bailian model catalog / pricing / params | `bl model list` | Console auth; `--model <family>` for detail, `--enrich` for input params (temperature/top_p…) |
| Validate / upload a training dataset | `bl dataset validate` / `upload` | API key; `.jsonl` or `.zip`; schemas: chatml/dpo/cpt/tts/image |
| Fine-tune a model (text/audio/image) | `bl finetune text\|audio\|image create` | API key; text = sft/sft-lora/dpo/dpo-lora/cpt; then `bl finetune watch` |
| Fine-tune job lifecycle | `bl finetune list`/`get`/`watch`/`logs`/`checkpoints`/`export`/`cancel`/`delete`/`capability` | API key |
| Deploy a (fine-tuned) model | `bl deploy text\|audio\|image create` | API key; audio defaults `--plan mu`, text/image `lora` |
| Deployment lifecycle | `bl deploy list`/`get`/`update`/`scale`/`delete`/`models` | API key |
| Declarative agent infra (agents.yaml) IaC lifecycle | `bl managed-agent init`/`validate`/`plan`/`apply`/`destroy` | `init` scaffolds agents.yaml, `validate` is offline, `plan` previews; `apply`/`destroy` mutate and require `--yes`; [`reference/managed-agent.md`](reference/managed-agent.md) |
| Chat with a managed agent (sessions) | `bl managed-agent session run`/`send`/`create`/`get`/`list`/`events`/`delete` | `run` = create + send + stream in one step; `send` targets an existing session; `events` lists history |
| Managed agent state inspection / adoption | `bl managed-agent state list`/`show`/`import`/`rm` | Local state ops; `import` adopts an existing remote resource; `rm` untracks without destroying remotely |
| Bailian MCP marketplace discovery / call | `bl mcp list` / `tools` / `call` | — |
| Bailian pipeline workflow (a step in a bl workflow) | `bl pipeline run` / `validate` | JSON/YAML workflow definitions |
| Bailian rate limits / quota | `bl quota list` / `check` / `request` | Console auth; class 2 — ask which product first if unnamed |
| Bailian free tier / usage stats | `bl usage free` / `stats` / `freetier` | Console auth; class 2 — ask which product first if unnamed |
| Console API (advanced) | `bl console call` | Console auth |
| Bailian workspace listing | `bl workspace list` | Console auth |
Commands not listed here: see [`reference/index.md`](reference/index.md) (**Quick index** / **By group**).
@@ -232,5 +235,6 @@ Full workflow, redaction rules, template, and exit-code reference: [`assets/issu
- Usage / quota / credits questions that do not name a product → ask which product (Bailian or another AI service) first; run `bl usage` / `bl quota` only after the user picks Bailian or Bailian context is already established.
- "Remember this" and memory requests default to the host agent's own memory; `bl memory *` is only for Bailian app memory resources.
- `bl file upload` and `bl pipeline run` are steps inside a Bailian workflow; do not use them to capture generic "upload this file" or "run a pipeline" requests.
- `bl managed-agent apply` / `destroy` mutate remote resources and only execute with `--yes`; run `plan` first and show the diff before confirming a mutation.
- When a matched `bl` command accepts a file URL, pass local paths directly; never require the user to host the file first.
- Console login → always `--console-site domestic|international`; see [`assets/setup.md`](assets/setup.md#console-site-selection).
+31 -30
View File
@@ -67,6 +67,7 @@ Use this index for the full quick index and global flags.
| `bl managed-agent session list` | List sessions from the provider | [managed-agent.md](managed-agent.md) |
| `bl managed-agent session run` | Create a session, send a message, and stream the response | [managed-agent.md](managed-agent.md) |
| `bl managed-agent session send` | Send a message to an existing session and stream the response | [managed-agent.md](managed-agent.md) |
| `bl managed-agent skill-list` | List skills from the provider's skill catalog | [managed-agent.md](managed-agent.md) |
| `bl managed-agent state import` | Import an existing remote resource into agents state | [managed-agent.md](managed-agent.md) |
| `bl managed-agent state list` | List resources tracked in agents state | [managed-agent.md](managed-agent.md) |
| `bl managed-agent state rm` | Remove a resource from state without destroying it remotely | [managed-agent.md](managed-agent.md) |
@@ -118,36 +119,36 @@ Use this index for the full quick index and global flags.
## By group
| Group | Commands | Reference |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| `advisor` | `recommend` | [advisor.md](advisor.md) |
| `app` | `call`, `list` | [app.md](app.md) |
| `auth` | `generate-access-token`, `login`, `logout`, `status` | [auth.md](auth.md) |
| `config` | `agent`, `list`, `set`, `show`, `ui`, `use` | [config.md](config.md) |
| `console` | `call` | [console.md](console.md) |
| `dataset` | `delete`, `get`, `list`, `upload`, `validate` | [dataset.md](dataset.md) |
| `deploy` | `audio create`, `delete`, `get`, `image create`, `list`, `models`, `scale`, `text create`, `update` | [deploy.md](deploy.md) |
| `file` | `upload` | [file.md](file.md) |
| `finetune` | `audio create`, `cancel`, `capability`, `checkpoints`, `delete`, `export`, `get`, `image create`, `list`, `logs`, `text create`, `watch` | [finetune.md](finetune.md) |
| `image` | `edit`, `generate` | [image.md](image.md) |
| `knowledge` | `chat`, `retrieve`, `search` | [knowledge.md](knowledge.md) |
| `managed-agent` | `apply`, `destroy`, `init`, `plan`, `session create`, `session delete`, `session events`, `session get`, `session list`, `session run`, `session send`, `state import`, `state list`, `state rm`, `state show`, `validate` | [managed-agent.md](managed-agent.md) |
| `mcp` | `call`, `list`, `tools` | [mcp.md](mcp.md) |
| `memory` | `add`, `delete`, `list`, `profile create`, `profile get`, `search`, `update` | [memory.md](memory.md) |
| `model` | `list` | [model.md](model.md) |
| `omni` | `(root)` | [omni.md](omni.md) |
| `pipeline` | `run`, `validate` | [pipeline.md](pipeline.md) |
| `plugin` | `install`, `link`, `list`, `remove` | [plugin.md](plugin.md) |
| `quota` | `check`, `history`, `list`, `request` | [quota.md](quota.md) |
| `search` | `web` | [search.md](search.md) |
| `speech` | `recognize`, `synthesize` | [speech.md](speech.md) |
| `text` | `chat` | [text.md](text.md) |
| `token-plan` | `add-member`, `assign-seats`, `create-key`, `list-seats` | [token-plan.md](token-plan.md) |
| `update` | `(root)` | [update.md](update.md) |
| `usage` | `free`, `freetier`, `stats`, `summary` | [usage.md](usage.md) |
| `video` | `download`, `edit`, `generate`, `ref`, `task get` | [video.md](video.md) |
| `vision` | `describe` | [vision.md](vision.md) |
| `workspace` | `init`, `list` | [workspace.md](workspace.md) |
| Group | Commands | Reference |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| `advisor` | `recommend` | [advisor.md](advisor.md) |
| `app` | `call`, `list` | [app.md](app.md) |
| `auth` | `generate-access-token`, `login`, `logout`, `status` | [auth.md](auth.md) |
| `config` | `agent`, `list`, `set`, `show`, `ui`, `use` | [config.md](config.md) |
| `console` | `call` | [console.md](console.md) |
| `dataset` | `delete`, `get`, `list`, `upload`, `validate` | [dataset.md](dataset.md) |
| `deploy` | `audio create`, `delete`, `get`, `image create`, `list`, `models`, `scale`, `text create`, `update` | [deploy.md](deploy.md) |
| `file` | `upload` | [file.md](file.md) |
| `finetune` | `audio create`, `cancel`, `capability`, `checkpoints`, `delete`, `export`, `get`, `image create`, `list`, `logs`, `text create`, `watch` | [finetune.md](finetune.md) |
| `image` | `edit`, `generate` | [image.md](image.md) |
| `knowledge` | `chat`, `retrieve`, `search` | [knowledge.md](knowledge.md) |
| `managed-agent` | `apply`, `destroy`, `init`, `plan`, `session create`, `session delete`, `session events`, `session get`, `session list`, `session run`, `session send`, `skill-list`, `state import`, `state list`, `state rm`, `state show`, `validate` | [managed-agent.md](managed-agent.md) |
| `mcp` | `call`, `list`, `tools` | [mcp.md](mcp.md) |
| `memory` | `add`, `delete`, `list`, `profile create`, `profile get`, `search`, `update` | [memory.md](memory.md) |
| `model` | `list` | [model.md](model.md) |
| `omni` | `(root)` | [omni.md](omni.md) |
| `pipeline` | `run`, `validate` | [pipeline.md](pipeline.md) |
| `plugin` | `install`, `link`, `list`, `remove` | [plugin.md](plugin.md) |
| `quota` | `check`, `history`, `list`, `request` | [quota.md](quota.md) |
| `search` | `web` | [search.md](search.md) |
| `speech` | `recognize`, `synthesize` | [speech.md](speech.md) |
| `text` | `chat` | [text.md](text.md) |
| `token-plan` | `add-member`, `assign-seats`, `create-key`, `list-seats` | [token-plan.md](token-plan.md) |
| `update` | `(root)` | [update.md](update.md) |
| `usage` | `free`, `freetier`, `stats`, `summary` | [usage.md](usage.md) |
| `video` | `download`, `edit`, `generate`, `ref`, `task get` | [video.md](video.md) |
| `vision` | `describe` | [vision.md](vision.md) |
| `workspace` | `init`, `list` | [workspace.md](workspace.md) |
## Global flags
@@ -20,6 +20,7 @@ Index: [index.md](index.md)
| `bl managed-agent session list` | List sessions from the provider |
| `bl managed-agent session run` | Create a session, send a message, and stream the response |
| `bl managed-agent session send` | Send a message to an existing session and stream the response |
| `bl managed-agent skill-list` | List skills from the provider's skill catalog |
| `bl managed-agent state import` | Import an existing remote resource into agents state |
| `bl managed-agent state list` | List resources tracked in agents state |
| `bl managed-agent state rm` | Remove a resource from state without destroying it remotely |
@@ -419,6 +420,50 @@ bl managed-agent session run --agent assistant --prompt "summarize this repo"
bl managed-agent session send --session-id sess_abc123 --message "continue"
```
### `bl managed-agent skill-list`
| Field | Value |
| --------------- | -------------------------------------------------------------------------------------------------- |
| **Name** | `managed-agent skill-list` |
| **Description** | List skills from the provider's skill catalog |
| **Usage** | `bl managed-agent skill-list [--source custom\|official\|all] [--provider <name>] [--file <path>]` |
#### Flags
| Flag | Type | Required | Description |
| ------------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------ |
| `--file <path>` | string | no | Config file path (default: agents.yaml) |
| `--source <source>` | string | no | Skill catalog: custom (workspace-uploaded, default), official (built-in), or all (both catalogs in one call) |
| `--provider <name>` | string | no | Target provider |
| `--api-key <key>` | string | no | API key |
| `--base-url <url>` | string | no | API base URL |
#### Notes
- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile).
- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json.
- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env.
- Providers without a skill listing API (e.g. ark) return an empty list.
- For agent-driven skill selection, use `--source all --output json`: one call returns both catalogs with per-skill `source` and `description` fields to pick from.
#### Examples
```bash
bl managed-agent skill-list
```
```bash
bl managed-agent skill-list --source official
```
```bash
bl managed-agent skill-list --source all --output json
```
```bash
bl managed-agent skill-list --source custom --provider bailian
```
### `bl managed-agent state import`
| Field | Value |