feat(knowledge): 支持上传目录路径并递归扫描文件

- 支持上传参数中传入目录路径,递归扫描子目录下文件
- 自动忽略 node_modules、.git 等常见工具目录
- 不支持的文件格式不会报错,跳过并列表提示
- 上传时校验扩展名和大小限制,支持批量文件上传
- 输出中增加跳过的文件列表,verbose 模式下显示详细文件名
- 测试覆盖目录上传、文件跳过和空目录等场景
- 更新相关文档,说明新支持的目录上传功能及注意事项
This commit is contained in:
zeyu.fz
2026-08-05 22:29:39 +08:00
parent e3bb5a7fa0
commit d9e8601a50
6 changed files with 423 additions and 152 deletions
@@ -25,13 +25,14 @@ import {
pollImportJob,
withPartialSuccessHint,
} from "./shared.ts";
import { checkUploadFile } from "./upload-support.ts";
import { checkUploadFile, expandUploadPaths } from "./upload-support.ts";
const DOC_UPLOAD_FLAGS = {
file: {
type: "array",
valueHint: "<path>",
description: "Local file path (repeatable). Extension and size validated before upload",
description:
"Local file or directory path (repeatable). Directories are scanned recursively; unsupported formats are skipped",
required: true,
},
indexId: {
@@ -67,18 +68,22 @@ interface UploadedFile {
}
export default defineCommand({
description: "Upload local files to the data center and optionally import into a knowledge base",
description:
"Upload local files or directories to the data center and optionally import into a knowledge base",
auth: "apiKey",
usageArgs: "--file <path> [flags]",
flags: DOC_UPLOAD_FLAGS,
notes: [
"Pipeline: apply upload lease → PUT to OSS → register file → (with --index-id) create import job.",
"Without --category-id the workspace default category is resolved automatically.",
"Directories are scanned recursively; node_modules, .git, and similar are skipped automatically.",
"Multiple files are processed sequentially; on failure, already-registered file ids are listed in the error hint.",
],
exampleArgs: [
"--file ./a.md --workspace-id ws-xxx",
"--file ./a.md --file ./b.pdf --index-id idx-xxx --wait",
"--file ./docs/ --workspace-id ws-xxx",
"--file ./docs/ --dry-run --verbose",
],
validate(flags) {
if (flags.wait && !flags.indexId) return "--wait requires --index-id";
@@ -89,9 +94,20 @@ export default defineCommand({
const workspaceId = resolveWorkspaceId(ctx);
const format = detectOutputFormat(settings.output);
// Expand directories into individual file paths; unsupported extensions are
// collected into `skipped` rather than throwing (directory-scan semantics)
const { files: expandedFiles, skipped } = expandUploadPaths(flags.file);
if (expandedFiles.length === 0) {
throw new BailianError(
"No supported files found",
ExitCode.USAGE,
`Supported formats: .pdf .doc .docx .ppt .pptx .xls .xlsx .csv .md .txt .html .png .jpg .jpeg .bmp .gif`,
);
}
// Local pre-flight validation also runs in dry-run (rehearsal semantics: surface
// file problems early); exceeding a soft limit only warns
const checkedFiles = flags.file.map((filePath) => {
const checkedFiles = expandedFiles.map((filePath) => {
const checked = checkUploadFile(filePath);
if (checked.warning) process.stderr.write(`Warning: ${checked.warning}\n`);
return { filePath, sizeBytes: checked.sizeBytes };
@@ -140,7 +156,7 @@ export default defineCommand({
} as unknown,
});
}
emitResult({ steps }, format);
emitResult({ steps, skipped }, format);
return;
}
@@ -278,12 +294,25 @@ export default defineCommand({
}
if (ingestionId) emitBare(`job: ${ingestionId}`);
if (finalStatus) emitBare(`status: ${finalStatus}`);
// Summary line: always show counts; list skipped files only with --verbose
const summaryParts = [`Uploaded ${uploaded.length} file${uploaded.length !== 1 ? "s" : ""}`];
if (skipped.length > 0) {
summaryParts.push(`skipped ${skipped.length} unsupported`);
}
emitBare(`\n${summaryParts.join(", ")}.`);
if (settings.verbose && skipped.length > 0) {
emitBare("Skipped files:");
for (const skippedPath of skipped) {
emitBare(` ${basename(skippedPath)}`);
}
}
return;
}
// An orchestration command has no single response to pass through — emit a custom stable shape
emitResult(
{
files: uploaded.map((item) => ({ path: item.path, fileId: item.fileId })),
skipped,
...(flags.indexId ? { index_id: flags.indexId } : {}),
...(ingestionId ? { ingestion_id: ingestionId } : {}),
...(finalStatus ? { final_status: finalStatus } : {}),
@@ -1,8 +1,8 @@
// Local pre-flight validation for file uploads (doc upload).
// Default category: the lease/addFile `category` parameter accepts the literal
// "default" (verified against the live API), so no listCategory resolution is needed.
import { readFileSync, statSync } from "node:fs";
import { basename, extname } from "node:path";
import { readFileSync, readdirSync, statSync } from "node:fs";
import { basename, extname, join } from "node:path";
import { BailianError, ExitCode } from "bailian-cli-core";
const MB = 1024 * 1024;
@@ -37,6 +37,105 @@ export const UPLOAD_FORMAT_RULES: Record<string, UploadFormatRule> = {
".html": { maxBytes: 10 * MB, enforce: "warn" },
};
/** Returns true when the extension is in the upload format allowlist. */
export function isSupportedExtension(filePath: string): boolean {
const extension = extname(filePath).toLowerCase();
return extension in UPLOAD_FORMAT_RULES;
}
/**
* Directory names skipped when expanding a directory path (recursive scan).
* Covers common tooling artifacts that should never contain user documents.
*/
const IGNORED_DIRECTORIES = new Set([
"node_modules",
".git",
".svn",
".hg",
"__pycache__",
".venv",
"venv",
".env",
".tox",
"dist",
"build",
".cache",
".next",
".nuxt",
]);
export interface ExpandResult {
files: string[];
skipped: string[];
}
/**
* Expand an array of paths into individual file paths.
* - Regular files are included as-is.
* - Directories are recursively scanned; files with unsupported extensions are
* collected into `skipped` instead of throwing.
* - Common tooling directories (node_modules, .git, …) are silently skipped.
* - A non-existent path throws USAGE so the user gets a clear error.
*/
export function expandUploadPaths(paths: string[]): ExpandResult {
const files: string[] = [];
const skipped: string[] = [];
function walkDirectory(directoryPath: string): void {
let entries: import("node:fs").Dirent[];
try {
entries = readdirSync(directoryPath, { withFileTypes: true });
} catch (error) {
const errno = (error as { code?: string }).code ?? "unknown";
throw new BailianError(
`Cannot read directory: ${directoryPath}`,
ExitCode.GENERAL,
`File system error (${errno}) — check the path and permissions.`,
);
}
for (const entry of entries) {
const entryFullPath = join(directoryPath, entry.name);
if (entry.isDirectory()) {
if (!IGNORED_DIRECTORIES.has(entry.name)) {
walkDirectory(entryFullPath);
}
continue;
}
if (entry.isFile()) {
if (isSupportedExtension(entry.name)) {
files.push(entryFullPath);
} else {
skipped.push(entryFullPath);
}
}
// Symlinks: withFileTypes follows symlinks for isFile/isDirectory,
// so they are handled by the branches above.
}
}
for (const inputPath of paths) {
let pathStat: import("node:fs").Stats;
try {
pathStat = statSync(inputPath);
} catch (error) {
const errno = (error as { code?: string }).code ?? "unknown";
throw new BailianError(
`Cannot read path: ${inputPath}`,
ExitCode.GENERAL,
`File system error (${errno}) — check the path and permissions.`,
);
}
if (pathStat.isDirectory()) {
walkDirectory(inputPath);
} else if (pathStat.isFile()) {
files.push(inputPath);
}
// Other types (socket, block device, etc.) are silently ignored.
}
return { files, skipped };
}
/** Local pre-flight check before reading the file: extension allowlist + hard/soft size limits. File I/O failure → GENERAL + errno hint. */
export function checkUploadFile(filePath: string): { sizeBytes: number; warning?: string } {
const extension = extname(filePath).toLowerCase();
@@ -1,4 +1,4 @@
import { mkdtempSync, writeFileSync } from "node:fs";
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, test } from "vite-plus/test";
@@ -104,11 +104,13 @@ describe("e2e: knowledge doc upload", () => {
"json",
]);
expect(exitCode, stderr).toBe(0);
const data = parseStdoutJson<{ steps: DryRunStep[] }>(stdout);
const data = parseStdoutJson<{ steps: DryRunStep[]; skipped: string[] }>(stdout);
expect(data.steps).toHaveLength(3);
const leaseRequest = data.steps[0]!.request as { sizeBytes?: unknown; category?: string };
expect(typeof leaseRequest.sizeBytes).toBe("string"); // gotcha: sizeBytes must be a string
expect(leaseRequest.category).toBe("cate_test");
// Single file path → no skipped entries
expect(data.skipped).toEqual([]);
});
test("--dry-run 带 --index-id 输出 4 步且 job 请求含显式 sourceType", async () => {
@@ -129,7 +131,7 @@ describe("e2e: knowledge doc upload", () => {
"json",
]);
expect(exitCode, stderr).toBe(0);
const data = parseStdoutJson<{ steps: DryRunStep[] }>(stdout);
const data = parseStdoutJson<{ steps: DryRunStep[]; skipped: string[] }>(stdout);
expect(data.steps).toHaveLength(4);
const jobRequest = data.steps[3]!.request as {
indexId?: string;
@@ -141,6 +143,56 @@ describe("e2e: knowledge doc upload", () => {
expect(jobRequest.indexId).toBe("idx_test");
expect(jobRequest).not.toHaveProperty("documentIds");
});
test("--file <dir> dry-run 展开目录且 skipped 包含不支持的文件", async () => {
const dirFixture = mkdtempSync(join(tmpdir(), "doc-upload-dir-e2e-"));
writeFileSync(join(dirFixture, "readme.md"), "# dir fixture\n");
writeFileSync(join(dirFixture, "data.csv"), "a,b,c");
writeFileSync(join(dirFixture, "config.json"), "{}"); // unsupported
const subDir = join(dirFixture, "sub");
mkdirSync(subDir);
writeFileSync(join(subDir, "notes.txt"), "hello");
const { stdout, stderr, exitCode } = await runCommandE2e(KNOWLEDGE_DOC_UPLOAD_ROUTES, [
"knowledge",
"doc",
"upload",
"--file",
dirFixture,
"--category-id",
"cate_test",
"--workspace-id",
"ws_test",
"--dry-run",
"--output",
"json",
]);
expect(exitCode, stderr).toBe(0);
const data = parseStdoutJson<{ steps: DryRunStep[]; skipped: string[] }>(stdout);
// 3 supported files × 3 steps each = 9 steps
expect(data.steps).toHaveLength(9);
// config.json is the only unsupported file
expect(data.skipped).toHaveLength(1);
expect(data.skipped[0]).toMatch(/config\.json$/);
});
test("--file <dir> 仅含不支持的文件报 USAGE", async () => {
const unsupportedDir = mkdtempSync(join(tmpdir(), "doc-upload-unsupported-"));
writeFileSync(join(unsupportedDir, "data.json"), "{}");
writeFileSync(join(unsupportedDir, "script.py"), "print(1)");
const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_DOC_UPLOAD_ROUTES, [
"knowledge",
"doc",
"upload",
"--file",
unsupportedDir,
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
expect(stderr).toMatch(/No supported files found/i);
});
});
// Live write artifacts (data-center files) are cleaned up in place via the file delete command.
@@ -1,10 +1,11 @@
import { mkdtempSync, writeFileSync } from "node:fs";
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, test } from "vite-plus/test";
import { ExitCode } from "bailian-cli-core";
import {
checkUploadFile,
expandUploadPaths,
UPLOAD_FORMAT_RULES,
} from "../../src/commands/knowledge/upload-support.ts";
@@ -60,3 +61,84 @@ describe("UPLOAD_FORMAT_RULES", () => {
expect(UPLOAD_FORMAT_RULES[".csv"]).toBeDefined(); // inconsistent across public docs; kept in the allowlist
});
});
describe("expandUploadPaths", () => {
const expandFixtureDir = mkdtempSync(join(tmpdir(), "expand-upload-"));
function setupExpandFixtures(): void {
// Root-level files
writeFileSync(join(expandFixtureDir, "readme.md"), "# root");
writeFileSync(join(expandFixtureDir, "data.csv"), "a,b,c");
writeFileSync(join(expandFixtureDir, "config.json"), "{}"); // unsupported
writeFileSync(join(expandFixtureDir, "script.py"), "print(1)"); // unsupported
// Subdirectory with files
const subDir = join(expandFixtureDir, "subdir");
mkdirSync(subDir);
writeFileSync(join(subDir, "notes.txt"), "hello");
writeFileSync(join(subDir, "archive.zip"), "PK"); // unsupported
// node_modules should be ignored
const nodeModulesDir = join(expandFixtureDir, "node_modules");
mkdirSync(nodeModulesDir);
writeFileSync(join(nodeModulesDir, "index.js"), "module.exports = {}");
// .git should be ignored
const gitDir = join(expandFixtureDir, ".git");
mkdirSync(gitDir);
writeFileSync(join(gitDir, "HEAD"), "ref: refs/heads/main");
}
setupExpandFixtures();
test("目录递归扫描: 支持的文件收集, 不支持的跳过, 忽略目录不进入", () => {
const result = expandUploadPaths([expandFixtureDir]);
const fileNames = result.files.map((filePath) => filePath.split("/").pop());
const skippedNames = result.skipped.map((filePath) => filePath.split("/").pop());
expect(fileNames).toContain("readme.md");
expect(fileNames).toContain("data.csv");
expect(fileNames).toContain("notes.txt");
// Unsupported files in root and subdir
expect(skippedNames).toContain("config.json");
expect(skippedNames).toContain("script.py");
expect(skippedNames).toContain("archive.zip");
// node_modules and .git contents should NOT appear
expect(fileNames).not.toContain("index.js");
expect(skippedNames).not.toContain("index.js");
expect(fileNames).not.toContain("HEAD");
expect(skippedNames).not.toContain("HEAD");
});
test("单个文件路径直接返回", () => {
const filePath = join(expandFixtureDir, "readme.md");
const result = expandUploadPaths([filePath]);
expect(result.files).toEqual([filePath]);
expect(result.skipped).toEqual([]);
});
test("混合文件和目录路径", () => {
const filePath = join(expandFixtureDir, "readme.md");
const result = expandUploadPaths([filePath, expandFixtureDir]);
// readme.md appears once from the direct file, once from the directory scan
const mdCount = result.files.filter((path) => path.endsWith("readme.md")).length;
expect(mdCount).toBe(2);
});
test("空目录返回空数组", () => {
const emptyDir = mkdtempSync(join(tmpdir(), "empty-upload-"));
const result = expandUploadPaths([emptyDir]);
expect(result.files).toEqual([]);
expect(result.skipped).toEqual([]);
});
test("不存在的路径抛 GENERAL 且 hint 含 errno", () => {
try {
expandUploadPaths([join(expandFixtureDir, "nonexistent.md")]);
expect.unreachable("should throw");
} catch (error) {
expect((error as { exitCode: number }).exitCode).toBe(ExitCode.GENERAL);
expect((error as { hint?: string }).hint).toMatch(/ENOENT/);
}
});
});
+89 -89
View File
@@ -9,95 +9,95 @@ Use this index for the skill-scoped quick index and global flags.
## Quick index
| Command | Description | Detail |
| -------------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------ |
| `bl advisor recommend` | Recommend the best models for your use case (intent analysis → candidate recall → LLM ranking) | [advisor.md](advisor.md) |
| `bl app call` | Call a Bailian application (agent or workflow) | [app.md](app.md) |
| `bl app list` | List Bailian applications | [app.md](app.md) |
| `bl auth generate-access-token` | Generate a CLI access token using OpenAPI AK/SK | [auth.md](auth.md) |
| `bl auth login` | Authenticate with API key, console browser login, or OpenAPI AK/SK (credentials can coexist) | [auth.md](auth.md) |
| `bl auth logout` | Clear stored credentials; full logout also clears the model Base URL | [auth.md](auth.md) |
| `bl auth status` | Show current authentication state | [auth.md](auth.md) |
| `bl config agent` | Configure a coding agent to use DashScope API | [config.md](config.md) |
| `bl config list` | List config profiles and show the active profile | [config.md](config.md) |
| `bl config set` | Set a config value | [config.md](config.md) |
| `bl config show` | Display current configuration | [config.md](config.md) |
| `bl config ui` | Open a local web UI to manage config profiles | [config.md](config.md) |
| `bl config use` | Set the active config profile | [config.md](config.md) |
| `bl console call` | Call a Bailian console API via the CLI gateway | [console.md](console.md) |
| `bl file upload` | Upload a local file to DashScope temporary storage (48h) | [file.md](file.md) |
| `bl knowledge category add` | Create a data-center category | [knowledge.md](knowledge.md) |
| `bl knowledge category delete` | Delete a data-center category | [knowledge.md](knowledge.md) |
| `bl knowledge category list` | List data-center categories | [knowledge.md](knowledge.md) |
| `bl knowledge chat` | Chat with a Bailian knowledge base (RAG Q&A with streaming) | [knowledge.md](knowledge.md) |
| `bl knowledge chunk add` | Add a chunk directly to a knowledge base | [knowledge.md](knowledge.md) |
| `bl knowledge chunk delete` | Delete chunks from a knowledge base (irreversible) | [knowledge.md](knowledge.md) |
| `bl knowledge chunk list` | List chunks in a knowledge base with content and status | [knowledge.md](knowledge.md) |
| `bl knowledge chunk update` | Update chunk content or toggle its retrieval visibility | [knowledge.md](knowledge.md) |
| `bl knowledge collection create` | Create a FILE data collection | [knowledge.md](knowledge.md) |
| `bl knowledge collection get` | Show data collection details | [knowledge.md](knowledge.md) |
| `bl knowledge create` | Create a knowledge base and import data-center files or categories | [knowledge.md](knowledge.md) |
| `bl knowledge delete` | Delete a knowledge base with all its documents and chunks | [knowledge.md](knowledge.md) |
| `bl knowledge doc delete` | Delete documents and their chunks from a knowledge base | [knowledge.md](knowledge.md) |
| `bl knowledge doc import-oss` | Batch import files from an authorized OSS bucket into the data center | [knowledge.md](knowledge.md) |
| `bl knowledge doc list` | List documents in a knowledge base with parse/index status | [knowledge.md](knowledge.md) |
| `bl knowledge doc status` | Check knowledge base import job status | [knowledge.md](knowledge.md) |
| `bl knowledge doc tag` | Batch update tags on data-center files | [knowledge.md](knowledge.md) |
| `bl knowledge doc upload` | Upload local files to the data center and optionally import into a knowledge base | [knowledge.md](knowledge.md) |
| `bl knowledge file delete` | Permanently delete a file from the data center | [knowledge.md](knowledge.md) |
| `bl knowledge file get` | Show data-center file details (size, MD5, tags, timestamps) | [knowledge.md](knowledge.md) |
| `bl knowledge file list` | List files in a data-center category | [knowledge.md](knowledge.md) |
| `bl knowledge info` | Show knowledge base configuration details | [knowledge.md](knowledge.md) |
| `bl knowledge list` | List knowledge bases in the workspace | [knowledge.md](knowledge.md) |
| `bl knowledge retrieve` | Retrieve from a Bailian knowledge base (deprecated, use `search` instead) | [knowledge.md](knowledge.md) |
| `bl knowledge search` | Search a Bailian knowledge base (RAG semantic retrieval) | [knowledge.md](knowledge.md) |
| `bl knowledge service copy` | Copy a service into a new draft (name gets a copy\_ prefix) | [knowledge.md](knowledge.md) |
| `bl knowledge service create` | Create a retrieval / Q&A service (initial status: draft, version: beta) | [knowledge.md](knowledge.md) |
| `bl knowledge service delete` | Delete a retrieval / Q&A service (soft delete, idempotent) | [knowledge.md](knowledge.md) |
| `bl knowledge service deploy` | Publish the beta draft of a service as a new version | [knowledge.md](knowledge.md) |
| `bl knowledge service get` | Show service (agent) details including per-version configuration | [knowledge.md](knowledge.md) |
| `bl knowledge service list` | List retrieval / Q&A services (agents) in the workspace | [knowledge.md](knowledge.md) |
| `bl knowledge service update` | Update service name, description or draft configuration | [knowledge.md](knowledge.md) |
| `bl knowledge stats` | Show knowledge base storage and QPS monitoring data | [knowledge.md](knowledge.md) |
| `bl knowledge update` | Update knowledge base name, description or rerank threshold | [knowledge.md](knowledge.md) |
| `bl mcp call` | Call a tool on an MCP server (tools/call) | [mcp.md](mcp.md) |
| `bl mcp list` | List MCP servers activated under your Bailian account | [mcp.md](mcp.md) |
| `bl mcp tools` | List tools exposed by an MCP server (tools/list) | [mcp.md](mcp.md) |
| `bl memory add` | Add memory from messages or custom content | [memory.md](memory.md) |
| `bl memory delete` | Delete a memory node | [memory.md](memory.md) |
| `bl memory list` | List memory nodes for a user | [memory.md](memory.md) |
| `bl memory profile create` | Create a user profile schema for memory profiling | [memory.md](memory.md) |
| `bl memory profile get` | Get user profile by schema ID and user ID | [memory.md](memory.md) |
| `bl memory search` | Search memory nodes by query or messages | [memory.md](memory.md) |
| `bl memory update` | Update a memory node content | [memory.md](memory.md) |
| `bl model list` | Browse model families or show detailed model info in the Bailian model marketplace | [model.md](model.md) |
| `bl pipeline run` | Run a pipeline workflow definition | [pipeline.md](pipeline.md) |
| `bl pipeline validate` | Validate a pipeline definition without executing | [pipeline.md](pipeline.md) |
| `bl plugin install` | Install or upgrade an allowlisted Command Pack | [plugin.md](plugin.md) |
| `bl plugin link` | Link an allowlisted local Command Pack for development | [plugin.md](plugin.md) |
| `bl plugin list` | List installed Command Packs and their load status | [plugin.md](plugin.md) |
| `bl plugin remove` | Remove an installed Command Pack | [plugin.md](plugin.md) |
| `bl quota check` | Check current usage against rate limits | [quota.md](quota.md) |
| `bl quota history` | View quota change history | [quota.md](quota.md) |
| `bl quota list` | View model RPM/TPM rate limits | [quota.md](quota.md) |
| `bl quota request` | Request a temporary quota increase | [quota.md](quota.md) |
| `bl search web` | Search the web using DashScope MCP WebSearch service | [search.md](search.md) |
| `bl skill add` | Install skills from the Bailian skill registry into local agents | [skill.md](skill.md) |
| `bl skill list` | List registry skills and diff against local installs | [skill.md](skill.md) |
| `bl skill remove` | Remove locally installed skills (registry is untouched) | [skill.md](skill.md) |
| `bl skill update` | Update installed skills to the latest registry versions | [skill.md](skill.md) |
| `bl text chat` | Send a chat completion (OpenAI compatible, DashScope) | [text.md](text.md) |
| `bl token-plan add-member` | Add a member to a Token Plan organization | [token-plan.md](token-plan.md) |
| `bl token-plan assign-seats` | Batch assign Token Plan seats to members | [token-plan.md](token-plan.md) |
| `bl token-plan create-key` | Create a Token Plan API key for a seat | [token-plan.md](token-plan.md) |
| `bl token-plan list-seats` | List Token Plan subscription seat details | [token-plan.md](token-plan.md) |
| `bl update` | Update the CLI to the latest or a specified version | [update.md](update.md) |
| `bl usage free` | Query free-tier quota for models (all models if --model is omitted) | [usage.md](usage.md) |
| `bl usage freetier` | Enable or disable auto-stop for free-tier models. Enables by default; use --off to disable | [usage.md](usage.md) |
| `bl usage stats` | Query model usage statistics | [usage.md](usage.md) |
| `bl usage summary` | Show a unified usage summary: free-tier quota and recent usage overview | [usage.md](usage.md) |
| `bl workspace init` | Initialize Bailian workspace and activate postpaid services | [workspace.md](workspace.md) |
| `bl workspace list` | List all workspaces | [workspace.md](workspace.md) |
| Command | Description | Detail |
| -------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------ |
| `bl advisor recommend` | Recommend the best models for your use case (intent analysis → candidate recall → LLM ranking) | [advisor.md](advisor.md) |
| `bl app call` | Call a Bailian application (agent or workflow) | [app.md](app.md) |
| `bl app list` | List Bailian applications | [app.md](app.md) |
| `bl auth generate-access-token` | Generate a CLI access token using OpenAPI AK/SK | [auth.md](auth.md) |
| `bl auth login` | Authenticate with API key, console browser login, or OpenAPI AK/SK (credentials can coexist) | [auth.md](auth.md) |
| `bl auth logout` | Clear stored credentials; full logout also clears the model Base URL | [auth.md](auth.md) |
| `bl auth status` | Show current authentication state | [auth.md](auth.md) |
| `bl config agent` | Configure a coding agent to use DashScope API | [config.md](config.md) |
| `bl config list` | List config profiles and show the active profile | [config.md](config.md) |
| `bl config set` | Set a config value | [config.md](config.md) |
| `bl config show` | Display current configuration | [config.md](config.md) |
| `bl config ui` | Open a local web UI to manage config profiles | [config.md](config.md) |
| `bl config use` | Set the active config profile | [config.md](config.md) |
| `bl console call` | Call a Bailian console API via the CLI gateway | [console.md](console.md) |
| `bl file upload` | Upload a local file to DashScope temporary storage (48h) | [file.md](file.md) |
| `bl knowledge category add` | Create a data-center category | [knowledge.md](knowledge.md) |
| `bl knowledge category delete` | Delete a data-center category | [knowledge.md](knowledge.md) |
| `bl knowledge category list` | List data-center categories | [knowledge.md](knowledge.md) |
| `bl knowledge chat` | Chat with a Bailian knowledge base (RAG Q&A with streaming) | [knowledge.md](knowledge.md) |
| `bl knowledge chunk add` | Add a chunk directly to a knowledge base | [knowledge.md](knowledge.md) |
| `bl knowledge chunk delete` | Delete chunks from a knowledge base (irreversible) | [knowledge.md](knowledge.md) |
| `bl knowledge chunk list` | List chunks in a knowledge base with content and status | [knowledge.md](knowledge.md) |
| `bl knowledge chunk update` | Update chunk content or toggle its retrieval visibility | [knowledge.md](knowledge.md) |
| `bl knowledge collection create` | Create a FILE data collection | [knowledge.md](knowledge.md) |
| `bl knowledge collection get` | Show data collection details | [knowledge.md](knowledge.md) |
| `bl knowledge create` | Create a knowledge base and import data-center files or categories | [knowledge.md](knowledge.md) |
| `bl knowledge delete` | Delete a knowledge base with all its documents and chunks | [knowledge.md](knowledge.md) |
| `bl knowledge doc delete` | Delete documents and their chunks from a knowledge base | [knowledge.md](knowledge.md) |
| `bl knowledge doc import-oss` | Batch import files from an authorized OSS bucket into the data center | [knowledge.md](knowledge.md) |
| `bl knowledge doc list` | List documents in a knowledge base with parse/index status | [knowledge.md](knowledge.md) |
| `bl knowledge doc status` | Check knowledge base import job status | [knowledge.md](knowledge.md) |
| `bl knowledge doc tag` | Batch update tags on data-center files | [knowledge.md](knowledge.md) |
| `bl knowledge doc upload` | Upload local files or directories to the data center and optionally import into a knowledge base | [knowledge.md](knowledge.md) |
| `bl knowledge file delete` | Permanently delete a file from the data center | [knowledge.md](knowledge.md) |
| `bl knowledge file get` | Show data-center file details (size, MD5, tags, timestamps) | [knowledge.md](knowledge.md) |
| `bl knowledge file list` | List files in a data-center category | [knowledge.md](knowledge.md) |
| `bl knowledge info` | Show knowledge base configuration details | [knowledge.md](knowledge.md) |
| `bl knowledge list` | List knowledge bases in the workspace | [knowledge.md](knowledge.md) |
| `bl knowledge retrieve` | Retrieve from a Bailian knowledge base (deprecated, use `search` instead) | [knowledge.md](knowledge.md) |
| `bl knowledge search` | Search a Bailian knowledge base (RAG semantic retrieval) | [knowledge.md](knowledge.md) |
| `bl knowledge service copy` | Copy a service into a new draft (name gets a copy\_ prefix) | [knowledge.md](knowledge.md) |
| `bl knowledge service create` | Create a retrieval / Q&A service (initial status: draft, version: beta) | [knowledge.md](knowledge.md) |
| `bl knowledge service delete` | Delete a retrieval / Q&A service (soft delete, idempotent) | [knowledge.md](knowledge.md) |
| `bl knowledge service deploy` | Publish the beta draft of a service as a new version | [knowledge.md](knowledge.md) |
| `bl knowledge service get` | Show service (agent) details including per-version configuration | [knowledge.md](knowledge.md) |
| `bl knowledge service list` | List retrieval / Q&A services (agents) in the workspace | [knowledge.md](knowledge.md) |
| `bl knowledge service update` | Update service name, description or draft configuration | [knowledge.md](knowledge.md) |
| `bl knowledge stats` | Show knowledge base storage and QPS monitoring data | [knowledge.md](knowledge.md) |
| `bl knowledge update` | Update knowledge base name, description or rerank threshold | [knowledge.md](knowledge.md) |
| `bl mcp call` | Call a tool on an MCP server (tools/call) | [mcp.md](mcp.md) |
| `bl mcp list` | List MCP servers activated under your Bailian account | [mcp.md](mcp.md) |
| `bl mcp tools` | List tools exposed by an MCP server (tools/list) | [mcp.md](mcp.md) |
| `bl memory add` | Add memory from messages or custom content | [memory.md](memory.md) |
| `bl memory delete` | Delete a memory node | [memory.md](memory.md) |
| `bl memory list` | List memory nodes for a user | [memory.md](memory.md) |
| `bl memory profile create` | Create a user profile schema for memory profiling | [memory.md](memory.md) |
| `bl memory profile get` | Get user profile by schema ID and user ID | [memory.md](memory.md) |
| `bl memory search` | Search memory nodes by query or messages | [memory.md](memory.md) |
| `bl memory update` | Update a memory node content | [memory.md](memory.md) |
| `bl model list` | Browse model families or show detailed model info in the Bailian model marketplace | [model.md](model.md) |
| `bl pipeline run` | Run a pipeline workflow definition | [pipeline.md](pipeline.md) |
| `bl pipeline validate` | Validate a pipeline definition without executing | [pipeline.md](pipeline.md) |
| `bl plugin install` | Install or upgrade an allowlisted Command Pack | [plugin.md](plugin.md) |
| `bl plugin link` | Link an allowlisted local Command Pack for development | [plugin.md](plugin.md) |
| `bl plugin list` | List installed Command Packs and their load status | [plugin.md](plugin.md) |
| `bl plugin remove` | Remove an installed Command Pack | [plugin.md](plugin.md) |
| `bl quota check` | Check current usage against rate limits | [quota.md](quota.md) |
| `bl quota history` | View quota change history | [quota.md](quota.md) |
| `bl quota list` | View model RPM/TPM rate limits | [quota.md](quota.md) |
| `bl quota request` | Request a temporary quota increase | [quota.md](quota.md) |
| `bl search web` | Search the web using DashScope MCP WebSearch service | [search.md](search.md) |
| `bl skill add` | Install skills from the Bailian skill registry into local agents | [skill.md](skill.md) |
| `bl skill list` | List registry skills and diff against local installs | [skill.md](skill.md) |
| `bl skill remove` | Remove locally installed skills (registry is untouched) | [skill.md](skill.md) |
| `bl skill update` | Update installed skills to the latest registry versions | [skill.md](skill.md) |
| `bl text chat` | Send a chat completion (OpenAI compatible, DashScope) | [text.md](text.md) |
| `bl token-plan add-member` | Add a member to a Token Plan organization | [token-plan.md](token-plan.md) |
| `bl token-plan assign-seats` | Batch assign Token Plan seats to members | [token-plan.md](token-plan.md) |
| `bl token-plan create-key` | Create a Token Plan API key for a seat | [token-plan.md](token-plan.md) |
| `bl token-plan list-seats` | List Token Plan subscription seat details | [token-plan.md](token-plan.md) |
| `bl update` | Update the CLI to the latest or a specified version | [update.md](update.md) |
| `bl usage free` | Query free-tier quota for models (all models if --model is omitted) | [usage.md](usage.md) |
| `bl usage freetier` | Enable or disable auto-stop for free-tier models. Enables by default; use --off to disable | [usage.md](usage.md) |
| `bl usage stats` | Query model usage statistics | [usage.md](usage.md) |
| `bl usage summary` | Show a unified usage summary: free-tier quota and recent usage overview | [usage.md](usage.md) |
| `bl workspace init` | Initialize Bailian workspace and activate postpaid services | [workspace.md](workspace.md) |
| `bl workspace list` | List all workspaces | [workspace.md](workspace.md) |
## By group
+61 -52
View File
@@ -7,42 +7,42 @@ Index: [index.md](index.md)
## Commands in this group
| Command | Description |
| -------------------------------- | --------------------------------------------------------------------------------- |
| `bl knowledge category add` | Create a data-center category |
| `bl knowledge category delete` | Delete a data-center category |
| `bl knowledge category list` | List data-center categories |
| `bl knowledge chat` | Chat with a Bailian knowledge base (RAG Q&A with streaming) |
| `bl knowledge chunk add` | Add a chunk directly to a knowledge base |
| `bl knowledge chunk delete` | Delete chunks from a knowledge base (irreversible) |
| `bl knowledge chunk list` | List chunks in a knowledge base with content and status |
| `bl knowledge chunk update` | Update chunk content or toggle its retrieval visibility |
| `bl knowledge collection create` | Create a FILE data collection |
| `bl knowledge collection get` | Show data collection details |
| `bl knowledge create` | Create a knowledge base and import data-center files or categories |
| `bl knowledge delete` | Delete a knowledge base with all its documents and chunks |
| `bl knowledge doc delete` | Delete documents and their chunks from a knowledge base |
| `bl knowledge doc import-oss` | Batch import files from an authorized OSS bucket into the data center |
| `bl knowledge doc list` | List documents in a knowledge base with parse/index status |
| `bl knowledge doc status` | Check knowledge base import job status |
| `bl knowledge doc tag` | Batch update tags on data-center files |
| `bl knowledge doc upload` | Upload local files to the data center and optionally import into a knowledge base |
| `bl knowledge file delete` | Permanently delete a file from the data center |
| `bl knowledge file get` | Show data-center file details (size, MD5, tags, timestamps) |
| `bl knowledge file list` | List files in a data-center category |
| `bl knowledge info` | Show knowledge base configuration details |
| `bl knowledge list` | List knowledge bases in the workspace |
| `bl knowledge retrieve` | Retrieve from a Bailian knowledge base (deprecated, use `search` instead) |
| `bl knowledge search` | Search a Bailian knowledge base (RAG semantic retrieval) |
| `bl knowledge service copy` | Copy a service into a new draft (name gets a copy\_ prefix) |
| `bl knowledge service create` | Create a retrieval / Q&A service (initial status: draft, version: beta) |
| `bl knowledge service delete` | Delete a retrieval / Q&A service (soft delete, idempotent) |
| `bl knowledge service deploy` | Publish the beta draft of a service as a new version |
| `bl knowledge service get` | Show service (agent) details including per-version configuration |
| `bl knowledge service list` | List retrieval / Q&A services (agents) in the workspace |
| `bl knowledge service update` | Update service name, description or draft configuration |
| `bl knowledge stats` | Show knowledge base storage and QPS monitoring data |
| `bl knowledge update` | Update knowledge base name, description or rerank threshold |
| Command | Description |
| -------------------------------- | ------------------------------------------------------------------------------------------------ |
| `bl knowledge category add` | Create a data-center category |
| `bl knowledge category delete` | Delete a data-center category |
| `bl knowledge category list` | List data-center categories |
| `bl knowledge chat` | Chat with a Bailian knowledge base (RAG Q&A with streaming) |
| `bl knowledge chunk add` | Add a chunk directly to a knowledge base |
| `bl knowledge chunk delete` | Delete chunks from a knowledge base (irreversible) |
| `bl knowledge chunk list` | List chunks in a knowledge base with content and status |
| `bl knowledge chunk update` | Update chunk content or toggle its retrieval visibility |
| `bl knowledge collection create` | Create a FILE data collection |
| `bl knowledge collection get` | Show data collection details |
| `bl knowledge create` | Create a knowledge base and import data-center files or categories |
| `bl knowledge delete` | Delete a knowledge base with all its documents and chunks |
| `bl knowledge doc delete` | Delete documents and their chunks from a knowledge base |
| `bl knowledge doc import-oss` | Batch import files from an authorized OSS bucket into the data center |
| `bl knowledge doc list` | List documents in a knowledge base with parse/index status |
| `bl knowledge doc status` | Check knowledge base import job status |
| `bl knowledge doc tag` | Batch update tags on data-center files |
| `bl knowledge doc upload` | Upload local files or directories to the data center and optionally import into a knowledge base |
| `bl knowledge file delete` | Permanently delete a file from the data center |
| `bl knowledge file get` | Show data-center file details (size, MD5, tags, timestamps) |
| `bl knowledge file list` | List files in a data-center category |
| `bl knowledge info` | Show knowledge base configuration details |
| `bl knowledge list` | List knowledge bases in the workspace |
| `bl knowledge retrieve` | Retrieve from a Bailian knowledge base (deprecated, use `search` instead) |
| `bl knowledge search` | Search a Bailian knowledge base (RAG semantic retrieval) |
| `bl knowledge service copy` | Copy a service into a new draft (name gets a copy\_ prefix) |
| `bl knowledge service create` | Create a retrieval / Q&A service (initial status: draft, version: beta) |
| `bl knowledge service delete` | Delete a retrieval / Q&A service (soft delete, idempotent) |
| `bl knowledge service deploy` | Publish the beta draft of a service as a new version |
| `bl knowledge service get` | Show service (agent) details including per-version configuration |
| `bl knowledge service list` | List retrieval / Q&A services (agents) in the workspace |
| `bl knowledge service update` | Update service name, description or draft configuration |
| `bl knowledge stats` | Show knowledge base storage and QPS monitoring data |
| `bl knowledge update` | Update knowledge base name, description or rerank threshold |
## Command details
@@ -660,30 +660,31 @@ bl knowledge doc tag --doc-id file-a --doc-id file-b --tag final --mode overwrit
### `bl knowledge doc upload`
| Field | Value |
| --------------- | --------------------------------------------------------------------------------- |
| **Name** | `knowledge doc upload` |
| **Description** | Upload local files to the data center and optionally import into a knowledge base |
| **Usage** | `bl knowledge doc upload --file <path> [flags]` |
| Field | Value |
| --------------- | ------------------------------------------------------------------------------------------------ |
| **Name** | `knowledge doc upload` |
| **Description** | Upload local files or directories to the data center and optionally import into a knowledge base |
| **Usage** | `bl knowledge doc upload --file <path> [flags]` |
#### Flags
| Flag | Type | Required | Description |
| --------------------------- | ------ | -------- | -------------------------------------------------------------------------- |
| `--file <path>` | array | yes | Local file path (repeatable). Extension and size validated before upload |
| `--index-id <id>` | string | no | Import into this knowledge base after registration (one job for all files) |
| `--category-id <id>` | string | no | Target data-center category; defaults to the workspace default category |
| `--tag <text>` | array | no | File tag (repeatable), applied to every uploaded file |
| `--wait` | switch | no | Poll the import job to a terminal state (needs --index-id) |
| `--poll-interval <seconds>` | number | no | Polling interval when waiting (default: 5) |
| `--workspace-id <id>` | string | no | Workspace ID for API endpoint URL (or set BAILIAN_WORKSPACE_ID) |
| `--api-key <key>` | string | no | API key |
| `--base-url <url>` | string | no | API base URL |
| Flag | Type | Required | Description |
| --------------------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------- |
| `--file <path>` | array | yes | Local file or directory path (repeatable). Directories are scanned recursively; unsupported formats are skipped |
| `--index-id <id>` | string | no | Import into this knowledge base after registration (one job for all files) |
| `--category-id <id>` | string | no | Target data-center category; defaults to the workspace default category |
| `--tag <text>` | array | no | File tag (repeatable), applied to every uploaded file |
| `--wait` | switch | no | Poll the import job to a terminal state (needs --index-id) |
| `--poll-interval <seconds>` | number | no | Polling interval when waiting (default: 5) |
| `--workspace-id <id>` | string | no | Workspace ID for API endpoint URL (or set BAILIAN_WORKSPACE_ID) |
| `--api-key <key>` | string | no | API key |
| `--base-url <url>` | string | no | API base URL |
#### Notes
- Pipeline: apply upload lease → PUT to OSS → register file → (with --index-id) create import job.
- Without --category-id the workspace default category is resolved automatically.
- Directories are scanned recursively; node_modules, .git, and similar are skipped automatically.
- Multiple files are processed sequentially; on failure, already-registered file ids are listed in the error hint.
#### Examples
@@ -696,6 +697,14 @@ bl knowledge doc upload --file ./a.md --workspace-id ws-xxx
bl knowledge doc upload --file ./a.md --file ./b.pdf --index-id idx-xxx --wait
```
```bash
bl knowledge doc upload --file ./docs/ --workspace-id ws-xxx
```
```bash
bl knowledge doc upload --file ./docs/ --dry-run --verbose
```
### `bl knowledge file delete`
| Field | Value |